From 43e94db4de502c63a0255dbbc253eae71e20120d Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 6 Jul 2023 17:39:59 +0200 Subject: [PATCH 001/143] Symbolic Outputs branch - new parameter dict & symbolic tensor --- notebooks/Symbolic Output.ipynb | 144 +++++++ qgs/inner_products/symbolic.py | 6 +- qgs/params/params.py | 49 ++- qgs/tensors/symbolic_qgtensor.py | 699 +++++++++++++++++++++++++++++++ 4 files changed, 894 insertions(+), 4 deletions(-) create mode 100644 notebooks/Symbolic Output.ipynb create mode 100644 qgs/tensors/symbolic_qgtensor.py diff --git a/notebooks/Symbolic Output.ipynb b/notebooks/Symbolic Output.ipynb new file mode 100644 index 0000000..95d3b18 --- /dev/null +++ b/notebooks/Symbolic Output.ipynb @@ -0,0 +1,144 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "12f59a3f", + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "sys.path.extend([os.path.abspath('../')])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7711e102", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.params.params import QgParams" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2a8e7c1", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters = QgParams(dynamic_T=True)\n", + "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", + "# Mode truncation at the wavenumber 2 in the x and at the \n", + "# wavenumber 4 in the y spatial coordinates for the ocean\n", + "model_parameters.set_oceanic_basin_fourier_modes(2, 4, mode=\"symbolic\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "006f3c7d", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.symbolic_params" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36811d45", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.symbolic_params['atm_gamma']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8500ea89", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.inner_products.analytic import AtmosphericAnalyticInnerProducts, OceanicAnalyticInnerProducts, GroundAnalyticInnerProducts\n", + "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, GroundSymbolicInnerProducts\n", + "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", + "from qgs.tensors.symbolic_qgtensor import SymbolicLinTensor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6112c83a", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters)\n", + "ocn_ip = OceanicSymbolicInnerProducts(model_parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81d1676b", + "metadata": {}, + "outputs": [], + "source": [ + "sym_ten = SymbolicLinTensor(model_parameters, atm_ip, ocn_ip)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8037bbb9", + "metadata": {}, + "outputs": [], + "source": [ + "sym_ten.LR" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76f0cb66", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.symbolic_params['atm_T0']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3e895e2", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 323d269..8a90062 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -104,7 +104,7 @@ class AtmosphericSymbolicInnerProducts(AtmosphericInnerProducts): """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, symbolic_returned=False): AtmosphericInnerProducts.__init__(self) @@ -647,7 +647,7 @@ class OceanicSymbolicInnerProducts(OceanicInnerProducts): symbolic computation. """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, symbolic_returned=False): OceanicInnerProducts.__init__(self) @@ -1036,7 +1036,7 @@ class GroundSymbolicInnerProducts(GroundInnerProducts): """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, symbolic_returned=False): GroundInnerProducts.__init__(self) diff --git a/qgs/params/params.py b/qgs/params/params.py index eeda008..d1cde66 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -50,7 +50,7 @@ from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis -from sympy import simplify +from sympy import simplify, Symbol # TODO: - store model version in a variable somewhere # - force the user to define the aspect ratio n at parameter object instantiation @@ -67,6 +67,8 @@ class Params(ABC): _name = "" + symbolic_params = dict() + def __init__(self, dic=None): self.set_params(dic) @@ -768,6 +770,8 @@ class QgParams(Params): Scale parameters instance. If `None`, create a new ScaleParams instance. Default to None. Default to `None`. + symbolic_params: dict(Symbolic Parameters), + A dictionary with the parameter names and symbolic variables. atmospheric_params: bool, None or AtmosphericParams, optional Atmospheric parameters instance. If 'True`, create a new AtmosphericParams instance. @@ -845,6 +849,49 @@ class QgParams(Params): """ _name = "General" + #//TODO: Should this dictionary be separated into three separate for atm, ocn, gnd? + symbolic_params = { + # Scale Parameters + 'L': Symbol('L'), + 'fo': Symbol('f0'), + 'beta': Symbol('beta'), + + # Atmosphere Parameters + 'kd': Symbol('k_d'), + 'kpd': Symbol('k_p'), + 'sigma': Symbol('sigma'), + + # Atmosphere Temp Parameters + 'hd': Symbol('hd'), + 'theta': Symbol('theta'), + 'atm_gamma': Symbol('gamma_a'), + 'atm_C': Symbol('C_a'), + 'eps': Symbol('epsilon'), + 'atm_T0': Symbol('T_a0'), + 'sc': Symbol('sc'), + 'hlambda': Symbol('lambda'), + + # Ground Parameters + 'hk': Symbol('h_k'), + + # Ground Temperature Parameters + 'gnd_gamma': Symbol('gamma_g'), + 'gnd_C': Symbol('C_g'), + 'gnd_T0': Symbol('T_g0'), + + # Ocean Parameters + 'gp': Symbol('g_p'), + 'r': Symbol('r'), + 'h': Symbol('h'), + 'd': Symbol('d'), + + # Ocean Temperature Parameters + 'ocn_gamma': Symbol('gamma_o'), + 'ocn_C': Symbol('C_o'), + 'ocn_T0': Symbol('T_o0') + + } + def __init__(self, dic=None, scale_params=None, atmospheric_params=True, atemperature_params=True, oceanic_params=None, otemperature_params=None, diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py new file mode 100644 index 0000000..0370caa --- /dev/null +++ b/qgs/tensors/symbolic_qgtensor.py @@ -0,0 +1,699 @@ +""" + symbolic qgs tensor module + ================= + + This module computes and holds the symbolic representation of the tensors representing the tendencies of the model's equations. + +""" +from contextlib import redirect_stdout + +import numpy as np +import sparse as sp +import sympy as sy +import pickle + +class SymbolicLinTensor(object): + """Symbolic qgs tendencies tensor class. + + Parameters + ---------- + params: None or QgParams, optional + The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. + atmospheric_inner_products: None or AtmosphericInnerProducts, optional + The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. + If None, disable the atmospheric tendencies. Default to `None`. + The inner product is returned in symbolic or numeric form. + oceanic_inner_products: None or OceanicInnerProducts, optional + The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. + If None, disable the oceanic tendencies. Default to `None`. + The inner product is returned in symbolic or numeric form. + ground_inner_products: None or GroundInnerProducts, optional + The inner products of the ground basis functions on which the model's PDE ground equations are projected. + If None, disable the ground tendencies. Default to `None`. + The inner product is returned in symbolic or numeric form. + + Attributes + ---------- + params: None or QgParams + The models parameters used to configure the tensor. `None` for an empty tensor. + atmospheric_inner_products: None or AtmosphericInnerProducts + The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. + If None, disable the atmospheric tendencies. Default to `None`. + oceanic_inner_products: None or OceanicInnerProducts + The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. + If None, disable the oceanic tendencies. Default to `None`. + ground_inner_products: None or GroundInnerProducts + The inner products of the ground basis functions on which the model's PDE ground equations are projected. + If None, disable the ground tendencies. Default to `None`. + tensor: sparse.COO(float) + The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. + jacobian_tensor: sparse.COO(float) + The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. + """ + + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): + + self.atmospheric_inner_products = atmospheric_inner_products + self.oceanic_inner_products = oceanic_inner_products + self.ground_inner_products = ground_inner_products + self.params = params + + self.sym_params = self.params.symbolic_params + + self.tensor = None + self.jacobian_tensor = None + + # self.compute_tensor() + + def _psi_a(self, i): + """Transform the :math:`\\psi_{\\mathrm a}` :math:`i`-th coefficient into the effective model's variable. + + Parameters + ---------- + i: int + The :math:`i`-th coefficients of :math:`\\psi_{\\mathrm a}` + + Returns + ------- + int + The effective model's variable. + """ + + return i + 1 + + def _theta_a(self, i): + """Transform the :math:`\\theta_{\\mathrm a}` :math:`i`-th coefficient into the effective model's variable. + + Parameters + ---------- + i: int + The :math:`i`-th coefficients of :math:`\\theta_{\\mathrm a}` + + Returns + ------- + int + The effective model's variable. + """ + return i + self.params.variables_range[0] + 1 + + def _psi_o(self, i): + """Transform the :math:`\\psi_{\\mathrm o}` :math:`i`-th coefficient into the effective model's variable. + + Parameters + ---------- + i: int + The :math:`i`-th coefficients of :math:`\\psi_{\\mathrm o}` + + Returns + ------- + int + The effective model's variable. + """ + return i + self.params.variables_range[1] + 1 + + def _deltaT_o(self, i): + """Transform the :math:`\\delta T_{\\mathrm o}` :math:`i`-th coefficient into the effective model's variable. + + Parameters + ---------- + i: int + The :math:`i`-th coefficients of :math:`\\delta T_{\\mathrm o}` + + Returns + ------- + int + The effective model's variable. + """ + return i + self.params.variables_range[2] + 1 + + def _deltaT_g(self, i): + """Transform the :math:`\\delta T_{\\mathrm o}` :math:`i`-th coefficient into the effective model's variable. + + Parameters + ---------- + i: int + The :math:`i`-th coefficients of :math:`\\delta T_{\\mathrm o}` + + Returns + ------- + int + The effective model's variable. + """ + return i + self.params.variables_range[1] + 1 + + @property + def sig0(self): + return self.sym_params['sigma'] / 2 + + @property + def LR(self): + return sy.sqrt(self.sym_params['gp'] * self.sym_params['h']) / self.sym_params['fo'] + + @property + def G(self): + return self.sym_params['L'] ** 2 / self.LR ** 2 + + @property + def Cpgo(self): + return self.sym_params['gnd_C'] / (self.sym_params['gamma_g'] * self.sym_params['fo']) * self.sym_params['rr'] / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) + + #//TODO: Finish the rest of the constant functions + + + def _compute_tensor_dicts(self): + + if self.params is None: + return None + + if self.atmospheric_inner_products is None and self.oceanic_inner_products is None \ + and self.ground_inner_products is None: + return None + + aips = self.atmospheric_inner_products + par = self.params + symbolic_params = self.params.symbolic_params + atp = par.atemperature_params + ap = par.atmospheric_params + op = par.oceanic_params + scp = par.scale_params + gp = par.ground_params + nvar = par.number_of_variables + ndim = par.ndim + + bips = None + if self.oceanic_inner_products is not None: + bips = self.oceanic_inner_products + ocean = True + else: + ocean = False + + if self.ground_inner_products is not None: + bips = self.ground_inner_products + ground_temp = True + else: + ground_temp = False + + if self.params.dynamic_T: + offset = 1 + else: + offset = 0 + + # constructing some derived matrices + if aips is not None: + a_inv = np.zeros((nvar[0], nvar[0])) + for i in range(offset, nvar[1]): + for j in range(offset, nvar[1]): + a_inv[i - offset, j - offset] = aips.a(i, j) + + a_inv = np.linalg.inv(a_inv) + a_inv = sp.COO(a_inv) + + a_theta = np.zeros((nvar[1], nvar[1])) + for i in range(nvar[1]): + for j in range(nvar[1]): + a_theta[i, j] = ap.sig0 * aips.a(i, j) - aips.u(i, j) + a_theta = np.linalg.inv(a_theta) + a_theta = sp.COO(a_theta) + + if bips is not None: + if ocean: + U_inv = np.zeros((nvar[3], nvar[3])) + for i in range(nvar[3]): + for j in range(nvar[3]): + U_inv[i, j] = bips.U(i, j) + U_inv = np.linalg.inv(U_inv) + U_inv = sp.COO(U_inv) + + M_psio = np.zeros((nvar[2], nvar[2])) + for i in range(offset, nvar[3]): + for j in range(offset, nvar[3]): + M_psio[i - offset, j - offset] = bips.M(i, j) + par.G * bips.U(i, j) + M_psio = np.linalg.inv(M_psio) + M_psio = sp.COO(M_psio) + else: + U_inv = np.zeros((nvar[2], nvar[2])) + for i in range(nvar[2]): + for j in range(nvar[2]): + U_inv[i, j] = bips.U(i, j) + U_inv = np.linalg.inv(U_inv) + U_inv = sp.COO(U_inv) + + ################# + + if bips is not None: + go = bips.stored + else: + go = True + + sparse_arrays_dict = dict() + + if aips.stored and go: + # psi_a part + for i in range(nvar[0]): + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + + for j in range(nvar[0]): + + jo = j + offset # skipping the theta 0 variable if it exists + + val = a_inv[i, :] @ aips._c[offset:, jo] + t[self._psi_a(j), 0] -= val * scp.beta + + t[self._psi_a(j), 0] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 + t[self._theta_a(jo), 0] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 + + if gp is not None: + if gp.hk is not None: + #//TODO: Need to make this symbolic + if gp.orographic_basis == "atmospheric": + oro = a_inv[i, :] @ aips._g[offset:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect + else: + #//TODO: Need to make this symbolic + # TODO: Can only be used with symbolic inner products here - a warning or an error should be raised if this is not the case. + oro = a_inv[i, :] @ aips._gh[offset:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect + t[self._psi_a(j), 0] -= oro / 2 + t[self._theta_a(jo), 0] += oro / 2 + + for k in range(nvar[0]): + ko = k + offset # skipping the theta 0 variable if it exists + val = a_inv[i, :] @ aips._b[offset:, jo, ko] + t[self._psi_a(j), self._psi_a(k)] = - val + t[self._theta_a(jo), self._theta_a(ko)] = - val + if ocean: + for j in range(nvar[2]): + jo = j + offset # skipping the theta 0 variable if it exists + val = a_inv[i, :] @ aips._d[offset:, jo] + t[self._psi_o(j), 0] += val * symbolic_params['kd'] / 2 + + sparse_arrays_dict[self._psi_a(i)] = t.to_coo() + + # theta_a part + for i in range(nvar[1]): + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + #//TODO: Need to make this symbolic + if par.Cpa is not None: + t[0, 0] -= a_theta[i, :] @ aips._u @ sp.COO(par.Cpa.astype(float)) # not perfect + #//TODO: Need to make this symbolic + if atp.hd is not None and atp.thetas is not None: + val = - a_theta[i, :] @ aips._u @ sp.COO(atp.thetas.astype(float)) # not perfect + t[0, 0] += val * symbolic_params['hd'] + + for j in range(nvar[0]): + + jo = j + offset # skipping the theta 0 variable if it exists + + val = a_theta[i, :] @ aips._a[:, jo] + t[self._psi_a(j), 0] += val * symbolic_params['kd'] * symbolic_params['sigma'] / 2 / 2 + t[self._theta_a(jo), 0] -= val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * symbolic_params['sigma'] / 2 + + val = - a_theta[i, :] @ aips._c[:, jo] + t[self._theta_a(jo), 0] += val * symbolic_params['beta'] * symbolic_params['sigma'] / 2 + + if gp is not None: + if gp.hk is not None: + #//TODO: Need to make this symbolic + if gp.orographic_basis == "atmospheric": + oro = a_theta[i, :] @ aips._g[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect + else: + oro = a_theta[i, :] @ aips._gh[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect + t[self._theta_a(jo), 0] -= symbolic_params['sigma'] / 2 * oro / 2 + t[self._psi_a(j), 0] += symbolic_params['sigma'] / 2 * oro / 2 + + for k in range(nvar[0]): + ko = k + offset # skipping the theta 0 variable if it exists + val = a_theta[i, :] @ aips._b[:, jo, ko] + t[self._psi_a(j), self._theta_a(ko)] = - val * symbolic_params['sigma'] / 2 + t[self._theta_a(jo), self._psi_a(k)] = - val * symbolic_params['sigma'] / 2 + + val = a_theta[i, :] @ aips._g[:, jo, ko] + t[self._psi_a(j), self._theta_a(ko)] += val + + for j in range(nvar[1]): + val = a_theta[i, :] @ aips._u[:, j] + if par.Lpa is not None: + t[self._theta_a(j), 0] += val * atp.sc * par.Lpa + if par.LSBpa is not None: + t[self._theta_a(j), 0] += val * par.LSBpa + + if atp.hd is not None: + t[self._theta_a(j), 0] += val * atp.hd + + if ocean: + for j in range(nvar[2]): + jo = j + offset # skipping the theta 0 variable if it exists + val = - a_theta[i, :] @ aips._d[:, jo] + t[self._psi_o(j), 0] += val * ap.sig0 * ap.kd / 2 + + if par.Lpa is not None: + for j in range(nvar[3]): + val = - a_theta[i, :] @ aips._s[:, j] + t[self._deltaT_o(j), 0] += val * par.Lpa / 2 + if par.LSBpgo is not None: + t[self._deltaT_o(j), 0] += val * par.LSBpgo + + if ground_temp: + if par.Lpa is not None: + for j in range(nvar[2]): + val = - a_theta[i, :] @ aips._s[:, j] + t[self._deltaT_g(j), 0] += val * par.Lpa / 2 + if par.LSBpgo is not None: + t[self._deltaT_g(j), 0] += val * par.LSBpgo + + sparse_arrays_dict[self._theta_a(i)] = t.to_coo() + + if ocean: + # psi_o part + for i in range(nvar[2]): + + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + + for j in range(nvar[0]): + jo = j + offset # skipping the theta 0 variable if it exists + val = M_psio[i, :] @ bips._K[offset:, jo] * op.d + t[self._psi_a(j), 0] += val + t[self._theta_a(jo), 0] -= val + + for j in range(nvar[2]): + jo = j + offset # skipping the T 0 variable if it exists + val = - M_psio[i, :] @ bips._N[offset:, jo] + t[self._psi_o(j), 0] += val * scp.beta + + val = - M_psio[i, :] @ bips._M[offset:, jo] + t[self._psi_o(j), 0] += val * (op.r + op.d) + + for k in range(nvar[2]): + ko = k + offset # skipping the T 0 variable if it exists + t[self._psi_o(j), self._psi_o(k)] -= M_psio[i, :] @ bips._C[offset:, jo, ko] + + sparse_arrays_dict[self._psi_o(i)] = t.to_coo() + + # deltaT_o part + for i in range(nvar[3]): + + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + t[0, 0] += U_inv[i, :] @ bips._W @ sp.COO(par.Cpgo.astype(float)) + + for j in range(nvar[1]): + val = U_inv[i, :] @ bips._W[:, j] + t[self._theta_a(j), 0] += val * 2 * atp.sc * par.Lpgo + if par.sbpa is not None: + t[self._theta_a(j), 0] += val * par.sbpa + + for j in range(nvar[3]): + t[self._deltaT_o(j), 0] = - par.Lpgo * _kronecker_delta(i, j) + if par.sbpgo is not None: + t[self._deltaT_o(j), 0] += - par.sbpgo * _kronecker_delta(i, j) + + for j in range(nvar[2]): + for k in range(nvar[2]): + jo = j + offset # skipping the T 0 variable if it exists + ko = k + offset # skipping the T 0 variable if it exists + t[self._psi_o(j), self._deltaT_o(ko)] -= U_inv[i, :] @ bips._O[:, jo, ko] + + sparse_arrays_dict[self._deltaT_o(i)] = t.to_coo() + + # deltaT_g part + if ground_temp: + for i in range(nvar[2]): + + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + t[0, 0] += U_inv[i, :] @ bips._W @ sp.COO(par.Cpgo.astype(float)) # not perfect + + for j in range(nvar[1]): + val = U_inv[i, :] @ bips._W[:, j] + t[self._theta_a(j), 0] += val * 2 * atp.sc * par.Lpgo + if par.sbpa is not None: + t[self._theta_a(j), 0] += val * par.sbpa + + for j in range(nvar[2]): + t[self._deltaT_g(j), 0] = - par.Lpgo * _kronecker_delta(i, j) + if par.sbpgo is not None: + t[self._deltaT_g(j), 0] += - par.sbpgo * _kronecker_delta(i, j) + + sparse_arrays_dict[self._deltaT_g(i)] = t.to_coo() + + else: + # psi_a part + for i in range(nvar[0]): + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + + for j in range(nvar[0]): + + jo = j + offset + + val = 0 + for jj in range(nvar[0]): + val += a_inv[i, jj] * aips.c(offset + jj, jo) + t[self._psi_a(j), 0] -= val * scp.beta + + t[self._psi_a(j), 0] -= (ap.kd * _kronecker_delta(i, j)) / 2 + t[self._theta_a(jo), 0] = (ap.kd * _kronecker_delta(i, j)) / 2 + + if gp is not None: + if gp.hk is not None: + oro = 0 + if gp.orographic_basis == "atmospheric": + for jj in range(nvar[0]): + for kk in range(nvar[0]): + oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * gp.hk[kk] + else: + for jj in range(nvar[0]): + for kk in range(nvar[0]): + oro += a_inv[i, jj] * aips.gh(offset + jj, j, offset + kk) * gp.hk[kk] + t[self._psi_a(j), 0] -= oro / 2 + t[self._theta_a(jo), 0] += oro / 2 + + for k in range(nvar[0]): + ko = k + offset # skipping the theta 0 variable if it exists + val = 0 + for jj in range(nvar[0]): + val += a_inv[i, jj] * aips.b(offset + jj, jo, ko) + t[self._psi_a(j), self._psi_a(k)] = - val + t[self._theta_a(jo), self._theta_a(ko)] = - val + if ocean: + for j in range(nvar[2]): + jo = j + offset + val = 0 + for jj in range(nvar[0]): + val += a_inv[i, jj] * aips.d(offset + jj, jo) + t[self._psi_o(j), 0] += val * ap.kd / 2 + + sparse_arrays_dict[self._psi_a(i)] = t.to_coo() + + # theta_a part + for i in range(nvar[1]): + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + + if par.Cpa is not None: + for jj in range(nvar[1]): + for kk in range(nvar[1]): + t[0, 0] -= a_theta[i, jj] * aips.u(jj, kk) * par.Cpa[kk] + + if atp.hd is not None and atp.thetas is not None: + val = 0 + for jj in range(nvar[1]): + for kk in range(nvar[1]): + val -= a_theta[i, jj] * aips.u(jj, kk) * atp.thetas[kk] + t[0, 0] += val * atp.hd + + for j in range(nvar[0]): + + jo = j + offset # skipping the theta 0 variable if it exists + + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips.a(jj, jo) + t[self._psi_a(j), 0] += val * ap.kd * ap.sig0 / 2 + t[self._theta_a(jo), 0] -= val * (ap.kd / 2 + 2 * ap.kdp) * ap.sig0 + + val = 0 + for jj in range(nvar[1]): + val -= a_theta[i, jj] * aips.c(jj, jo) + t[self._theta_a(jo), 0] += val * scp.beta * ap.sig0 + + if gp is not None: + if gp.hk is not None: + oro = 0 + if gp.orographic_basis == "atmospheric": + for jj in range(nvar[1]): + for kk in range(nvar[0]): + oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * gp.hk[kk] + else: + for jj in range(nvar[1]): + for kk in range(nvar[0]): + oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * gp.hk[kk] + t[self._theta_a(jo), 0] -= ap.sig0 * oro / 2 + t[self._psi_a(j), 0] += ap.sig0 * oro / 2 + + for k in range(nvar[0]): + ko = k + offset # skipping the theta 0 variable if it exists + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips.b(jj, jo, ko) + t[self._psi_a(j), self._theta_a(ko)] = - val * ap.sig0 + t[self._theta_a(jo), self._psi_a(k)] = - val * ap.sig0 + + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips.g(jj, jo, ko) + + t[self._psi_a(j), self._theta_a(ko)] += val + + for j in range(nvar[1]): + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips.u(jj, j) + + if par.Lpa is not None: + t[self._theta_a(j), 0] += val * atp.sc * par.Lpa + if par.LSBpa is not None: + t[self._theta_a(j), 0] += val * par.LSBpa + + if atp.hd is not None: + t[self._theta_a(j), 0] += val * atp.hd + + if ocean: + for j in range(nvar[2]): + jo = j + offset # skipping the theta 0 variable if it exists + val = 0 + for jj in range(nvar[1]): + val -= a_theta[i, jj] * aips.d(jj, jo) + t[self._psi_o(j), 0] += val * ap.sig0 * ap.kd / 2 + + if par.Lpa is not None: + for j in range(nvar[3]): + val = 0 + for jj in range(nvar[1]): + val -= a_theta[i, jj] * aips.s(jj, j) + t[self._deltaT_o(j), 0] += val * par.Lpa / 2 + if par.LSBpgo is not None: + t[self._deltaT_o(j), 0] += val * par.LSBpgo + + if ground_temp: + if par.Lpa is not None: + for j in range(nvar[2]): + val = 0 + for jj in range(nvar[1]): + val -= a_theta[i, jj] * aips.s(jj, j) + t[self._deltaT_g(j), 0] += val * par.Lpa / 2 + if par.LSBpgo is not None: + t[self._deltaT_g(j), 0] += val * par.LSBpgo + + sparse_arrays_dict[self._theta_a(i)] = t.to_coo() + + if ocean: + # psi_o part + for i in range(nvar[2]): + + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + + for j in range(nvar[0]): + jo = j + offset # skipping the theta 0 variable if it exists + + for jj in range(nvar[2]): + val = M_psio[i, jj] * bips.K(offset + jj, jo) * op.d + t[self._psi_a(j), 0] += val + t[self._theta_a(jo), 0] -= val + + for j in range(nvar[2]): + jo = j + offset # skipping the T 0 variable if it exists + + val = 0 + for jj in range(nvar[2]): + val -= M_psio[i, jj] * bips.N(offset + jj, jo) + t[self._psi_o(j), 0] += val * scp.beta + + val = 0 + for jj in range(nvar[2]): + val -= M_psio[i, jj] * bips.M(offset + jj, jo) + t[self._psi_o(j), 0] += val * (op.r + op.d) + + for k in range(nvar[2]): + ko = k + offset # skipping the T 0 variable if it exists + for jj in range(nvar[2]): + t[self._psi_o(j), self._psi_o(k)] -= M_psio[i, jj] * bips.C(offset + jj, jo, ko) + + sparse_arrays_dict[self._psi_o(i)] = t.to_coo() + + # deltaT_o part + for i in range(nvar[3]): + + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + for jj in range(nvar[1]): + for kk in range(nvar[3]): + t[0, 0] += U_inv[i, kk] * bips.W(kk, jj) * par.Cpgo[jj] + + for j in range(nvar[1]): + val = 0 + for jj in range(nvar[3]): + val += U_inv[i, jj] * bips.W(jj, j) + t[self._theta_a(j), 0] += val * 2 * atp.sc * par.Lpgo + if par.sbpa is not None: + t[self._theta_a(j), 0] += val * par.sbpa + + for j in range(nvar[3]): + t[self._deltaT_o(j), 0] = - par.Lpgo * _kronecker_delta(i, j) + if par.sbpgo is not None: + t[self._deltaT_o(j), 0] += - par.sbpgo * _kronecker_delta(i, j) + + for j in range(nvar[2]): + for k in range(offset, nvar[3]): + jo = j + offset # skipping the T 0 variable if it exists + for jj in range(nvar[3]): + t[self._psi_o(j), self._deltaT_o(k)] -= U_inv[i, jj] * bips.O(jj, jo, k) + + sparse_arrays_dict[self._deltaT_o(i)] = t.to_coo() + + # deltaT_g part + if ground_temp: + for i in range(nvar[2]): + + t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + for jj in range(nvar[1]): + for kk in range(nvar[2]): + t[0, 0] += U_inv[i, kk] * bips.W(kk, jj) * par.Cpgo[jj] + + for j in range(nvar[1]): + val = 0 + for jj in range(nvar[2]): + val += U_inv[i, jj] * bips.W(jj, j) + t[self._theta_a(j), 0] += val * 2 * atp.sc * par.Lpgo + if par.sbpa is not None: + t[self._theta_a(j), 0] += val * par.sbpa + + for j in range(nvar[2]): + t[self._deltaT_g(j), 0] = - par.Lpgo * _kronecker_delta(i, j) + if par.sbpgo is not None: + t[self._deltaT_g(j), 0] += - par.sbpgo * _kronecker_delta(i, j) + + sparse_arrays_dict[self._deltaT_g(i)] = t.to_coo() + + return sparse_arrays_dict + + def compute_tensor(self): + """Routine to compute the tensor.""" + # gathering + par = self.params + ndim = par.ndim + + sparse_arrays_dict = self._compute_tensor_dicts() + + tensor = sp.zeros((ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='coo') + if sparse_arrays_dict is not None: + tensor = self._add_dict_to_tensor(sparse_arrays_dict, tensor) + self._set_tensor(tensor) + + def _set_tensor(self, tensor): + if not isinstance(tensor, sp.COO): + tensor = tensor.to_coo() + self.jacobian_tensor = self.jacobian_from_tensor(tensor) + self.tensor = self.simplify_tensor(tensor) + + +def _kronecker_delta(i, j): + + if i == j: + return 1 + + else: + return 0 \ No newline at end of file From 2e8b461792f77be87130f04ac88d1d3157719463 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 7 Jul 2023 14:52:54 +0200 Subject: [PATCH 002/143] Working symbolic parameters --- qgs/params/params.py | 94 ++++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 29 deletions(-) diff --git a/qgs/params/params.py b/qgs/params/params.py index d1cde66..16feb91 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -50,7 +50,7 @@ from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis -from sympy import simplify, Symbol +import sympy as sy # TODO: - store model version in a variable somewhere # - force the user to define the aspect ratio n at parameter object instantiation @@ -852,43 +852,46 @@ class QgParams(Params): #//TODO: Should this dictionary be separated into three separate for atm, ocn, gnd? symbolic_params = { # Scale Parameters - 'L': Symbol('L'), - 'fo': Symbol('f0'), - 'beta': Symbol('beta'), + 'L': sy.Symbol('L'), + 'fo': sy.Symbol('f0'), + 'beta': sy.Symbol('beta'), # Atmosphere Parameters - 'kd': Symbol('k_d'), - 'kpd': Symbol('k_p'), - 'sigma': Symbol('sigma'), + 'kd': sy.Symbol('k_d'), + 'kpd': sy.Symbol('k_p'), + 'sigma': sy.Symbol('sigma'), # Atmosphere Temp Parameters - 'hd': Symbol('hd'), - 'theta': Symbol('theta'), - 'atm_gamma': Symbol('gamma_a'), - 'atm_C': Symbol('C_a'), - 'eps': Symbol('epsilon'), - 'atm_T0': Symbol('T_a0'), - 'sc': Symbol('sc'), - 'hlambda': Symbol('lambda'), + 'hd': sy.Symbol('hd'), + 'theta': sy.Symbol('theta'), + 'atm_gamma': sy.Symbol('gamma_a'), + 'atm_C_val': sy.Symbol('C_a'), + 'atm_C': None, + 'eps': sy.Symbol('epsilon'), + 'atm_T0': sy.Symbol('T_a0'), + 'sc': sy.Symbol('sc'), + 'hlambda': sy.Symbol('lambda'), # Ground Parameters - 'hk': Symbol('h_k'), + 'hk': sy.Symbol('h_k'), # Ground Temperature Parameters - 'gnd_gamma': Symbol('gamma_g'), - 'gnd_C': Symbol('C_g'), - 'gnd_T0': Symbol('T_g0'), + 'gnd_gamma': sy.Symbol('gamma_g'), + 'gnd_C_val': sy.Symbol('C_g'), + 'gnd_C': None, + 'gnd_T0': sy.Symbol('T_g0'), # Ocean Parameters - 'gp': Symbol('g_p'), - 'r': Symbol('r'), - 'h': Symbol('h'), - 'd': Symbol('d'), + 'gp': sy.Symbol('g_p'), + 'r': sy.Symbol('r'), + 'h': sy.Symbol('h'), + 'd': sy.Symbol('d'), # Ocean Temperature Parameters - 'ocn_gamma': Symbol('gamma_o'), - 'ocn_C': Symbol('C_o'), - 'ocn_T0': Symbol('T_o0') + 'ocn_gamma': sy.Symbol('gamma_o'), + 'ocn_C_val': sy.Symbol('C_o'), + 'ocn_C': None, + 'ocn_T0': sy.Symbol('T_o0') } @@ -1246,6 +1249,39 @@ def print_params(self): print("=============================\n") print(s) + def symbolic_insolation_array(self, Cpa=None, Cpgo=None): + """Set the array Cpa and Cpgo from given data, or the default symbols + + Parameters + ---------- + values: List(sympy Symbol) + A list of the sympy symbols that will be converted to an ImmutableSparseNDimArray. + + """ + if Cpa is not None: + atm_C_list = Cpa + elif Cpgo is not None: + gnd_C_list = Cpgo + ocn_C_list = Cpgo + else: + atm_C_list = [0] * self.nmod[0] + gnd_C_list = [0] * self.nmod[0] + ocn_C_list = [0] * self.nmod[0] + + + atm_C_list[0] = self.symbolic_params['atm_C_val'] + gnd_C_list[0] = self.symbolic_params['gnd_C_val'] + ocn_C_list[0] = self.symbolic_params['ocn_C_val'] + + if self.dynamic_T: + atm_C_list[1] = self.symbolic_params['atm_C_val'] + gnd_C_list[1] = self.symbolic_params['gnd_C_val'] + ocn_C_list[1] = self.symbolic_params['ocn_C_val'] + + self.symbolic_params['atm_C'] = sy.tensor.array.ImmutableSparseNDimArray(atm_C_list) + self.symbolic_params['gnd_C'] = sy.tensor.array.ImmutableSparseNDimArray(gnd_C_list) + self.symbolic_params['ocn_C'] = sy.tensor.array.ImmutableSparseNDimArray(ocn_C_list) + @property def ndim(self): """int: Total number of variables of the model.""" @@ -1367,7 +1403,7 @@ def atmospheric_basis(self, basis): self._atmospheric_basis = basis self._number_of_atmospheric_modes = len(basis.functions) if self.dynamic_T: - self._atmospheric_basis.functions.insert(0, simplify("1")) + self._atmospheric_basis.functions.insert(0, sy.simplify("1")) if self.ground_params is not None and self.ground_params.orographic_basis == "atmospheric": self.ground_params.set_orography(self._number_of_atmospheric_modes * [0.e0]) @@ -1390,7 +1426,7 @@ def oceanic_basis(self, basis): self._number_of_ground_modes = 0 self._number_of_oceanic_modes = len(basis) if self.dynamic_T: - self._oceanic_basis.functions.insert(0, simplify("1")) + self._oceanic_basis.functions.insert(0, sy.simplify("1")) if self.atemperature_params is not None: # disable the Newtonian cooling @@ -1447,7 +1483,7 @@ def ground_basis(self, basis): self._number_of_ground_modes = len(basis) self._number_of_oceanic_modes = 0 if self.dynamic_T: - self._ground_basis.functions.insert(0, simplify("1")) + self._ground_basis.functions.insert(0, sy.simplify("1")) if self.atemperature_params is not None: # disable the Newtonian cooling From e1d6d5df030d370596e426e2e439209df9c10949 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 7 Jul 2023 18:06:47 +0200 Subject: [PATCH 003/143] Update to ip - working for symbolic, broken the numeric --- qgs/inner_products/symbolic.py | 162 ++++++++++++++++++++++--------- qgs/params/params.py | 1 + qgs/tensors/symbolic_qgtensor.py | 151 ++++++++++++++++++---------- 3 files changed, 221 insertions(+), 93 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 8a90062..20ea0d8 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -31,13 +31,12 @@ from qgs.params.params import QgParams from qgs.inner_products.base import AtmosphericInnerProducts, OceanicInnerProducts, GroundInnerProducts from qgs.inner_products.definition import StandardSymbolicInnerProductDefinition -from sympy import lambdify from scipy.integrate import dblquad -from sympy import symbols +import sympy as sy # TODO: - Add warnings if trying to connect analytic and symbolic inner products together -_n = symbols('n', real=True, nonnegative=True) +_n = sy.Symbol('n', positive=True) class AtmosphericSymbolicInnerProducts(AtmosphericInnerProducts): @@ -104,7 +103,7 @@ class AtmosphericSymbolicInnerProducts(AtmosphericInnerProducts): """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, symbolic_returned=False): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False): AtmosphericInnerProducts.__init__(self) @@ -173,7 +172,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.ground_basis = None self.connected_to_ground = False - self.subs = [(_n, self.n)] + self.return_symbolic = return_symbolic + if return_symbolic: + self._p_compute = _symbolic_compute + self.subs = [('n', params.symbolic_params['n'])] + else: + self._p_compute = _parallel_compute + self.subs = [('n', self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() @@ -235,7 +240,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - + subs = self.subs + self.atmospheric_basis.substitutions + self.oceanic_basis.substitutions noc = len(ocean_basis) @@ -249,31 +254,41 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): args_list = [[(i, j), self.iip.ip_lap, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(noc)] - _parallel_compute(pool, args_list, subs, self._d, timeout) + output = self._p_compute(pool, args_list, subs, self._d, timeout) + if self.return_symbolic: + self._d = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, noc, output) + else: + self._d = self._d.to_coo() # s inner products args_list = [[(i, j), self.iip.symbolic_inner_product, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(noc)] - _parallel_compute(pool, args_list, subs, self._s, timeout) + output = self._p_compute(pool, args_list, subs, self._s, timeout) + if self.return_symbolic: + self._s = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, noc, output) + else: + self._s = self._s.to_coo() if self._T4: # v inner products args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.natm) for j in range(noc) for k in range(j, noc) for ell in range(k, noc) for m in range(ell, noc)] - _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._v, timeout, permute=True) elif self._dynamic_T: # v inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.natm) for m in range(noc)] - _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._v, timeout, permute=True) - self._s = self._s.to_coo() - self._d = self._d.to_coo() - if self._T4 or self._dynamic_T: - self._v = self._v.to_coo() + + if self._T4 or self._dynamic_T: + if self.return_symbolic: + self._v = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, noc, noc, noc, noc)) + else: + self._v = self._v.to_coo() def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, timeout=None): """Connect the atmosphere to the ground. @@ -326,33 +341,43 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti args_list = [[(i, j), self.iip.symbolic_inner_product, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(ngr)] - _parallel_compute(pool, args_list, subs, self._s, timeout) + output = self._p_compute(pool, args_list, subs, self._s, timeout) + if self.return_symbolic: + self._s = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, ngr, output) + else: + self._s = self._s.to_coo() + # gh inner products if orographic_basis != "atmospheric": args_list = [[(i, j, k), self.iip.ip_jac, (self._F(i), self._F(j), self._phi(k))] for i in range(self.natm) for j in range(self.natm) for k in range(ngr)] - _parallel_compute(pool, args_list, subs, self._gh, timeout) + output = self._p_compute(pool, args_list, subs, self._gh, timeout) + if self.return_symbolic: + self._gh = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, ngr)) + else: + if self._gh is not None: + self._gh = self._gh.to_coo() if self._T4: # v inner products args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.natm) for j in range(ngr) for k in range(j, ngr) for ell in range(k, ngr) for m in range(ell, ngr)] - _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._v, timeout, permute=True) elif self._dynamic_T: # v inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.natm) for m in range(ngr)] - _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._v, timeout, permute=True) - self._s = self._s.to_coo() - if self._gh is not None: - self._gh = self._gh.to_coo() - if self._T4 or self._dynamic_T: - self._v = self._v.to_coo() + if self._T4 or self._dynamic_T: + if self.return_symbolic: + self._v = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, ngr, ngr, ngr, ngr)) + else: + self._v = self._v.to_coo() def compute_inner_products(self, num_threads=None, timeout=None): """Function computing and storing all the inner products at once. @@ -382,59 +407,77 @@ def compute_inner_products(self, num_threads=None, timeout=None): num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - - subs = self.subs + self.atmospheric_basis.substitutions - + #//TODO: Fix substitution here, temporary fix + subs = self.subs #+ self.atmospheric_basis.substitutions + # a inner products args_list = [[(i, j), self.ip.ip_lap, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - - _parallel_compute(pool, args_list, subs, self._a, timeout) + + output = self._p_compute(pool, args_list, subs, self._a, timeout) + if self.return_symbolic: + self._a = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) + else: + self._a = self._a.to_coo() # u inner products args_list = [[(i, j), self.ip.symbolic_inner_product, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - _parallel_compute(pool, args_list, subs, self._u, timeout) + output = self._p_compute(pool, args_list, subs, self._u, timeout) + if self.return_symbolic: + self._u = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) + else: + self._u = self._u.to_coo() # c inner products args_list = [[(i, j), self.ip.ip_diff_x, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - _parallel_compute(pool, args_list, subs, self._c, timeout) + output = self._p_compute(pool, args_list, subs, self._c, timeout) + if self.return_symbolic: + self._c = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) + else: + self._c = self._c.to_coo() # b inner products args_list = [[(i, j, k), self.ip.ip_jac_lap, (self._F(i), self._F(j), self._F(k))] for i in range(self.natm) for j in range(self.natm) for k in range(self.natm)] - _parallel_compute(pool, args_list, subs, self._b, timeout) + output = self._p_compute(pool, args_list, subs, self._b, timeout) + if self.return_symbolic: + self._b = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) + else: + self._b = self._b.to_coo() # g inner products args_list = [[(i, j, k), self.ip.ip_jac, (self._F(i), self._F(j), self._F(k))] for i in range(self.natm) for j in range(self.natm) for k in range(self.natm)] - _parallel_compute(pool, args_list, subs, self._g, timeout) + output = self._p_compute(pool, args_list, subs, self._g, timeout) + if self.return_symbolic: + self._g = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) + else: + self._g = self._g.to_coo() if self._T4: # z inner products args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.natm) for j in range(self.natm) for k in range(j, self.natm) for ell in range(k, self.natm) for m in range(ell, self.natm)] - _parallel_compute(pool, args_list, subs, self._z, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._z, timeout, permute=True) elif self._dynamic_T: # z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.natm) for m in range(self.natm)] - _parallel_compute(pool, args_list, subs, self._z, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._z, timeout, permute=True) - self._a = self._a.to_coo() - self._u = self._u.to_coo() - self._c = self._c.to_coo() - self._g = self._g.to_coo() - self._b = self._b.to_coo() - if self._T4 or self._dynamic_T: - self._z = self._z.to_coo() + if self._T4 or self._dynamic_T: + if self.return_symbolic: + self._z = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm, self.natm, self.natm)) + else: + self._z = self._z.to_coo() @property def natm(self): @@ -1326,8 +1369,8 @@ def _num_apply(ls): integrand = ls[1](*ls[2], integrand=True) num_integrand = integrand[0].subs(ls[3]) - func = lambdify((integrand[1][0], integrand[2][0]), num_integrand, 'numpy') - + func = sy.lambdify((integrand[1][0], integrand[2][0]), num_integrand, 'numpy') + try: a = integrand[2][1].subs(ls[3]) except: @@ -1375,7 +1418,7 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False timeout = None if timeout is not True: - future = pool.map(_apply, args_list, timeout=timeout) + future = pool.map(_apply, args_list, timeout=None) results = future.result() num_args_list = list() i = 0 @@ -1413,6 +1456,37 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False except StopIteration: break +def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False): + result_list = list() + + #//TODO: I am not sure what this timeout does, I have switched if off as it was leading to non-convergence. + future = pool.map(_apply, args_list, timeout=None) + results = future.result() + num_args_list = list() + i = 0 + while True: + try: + res = next(results) + result_list.append(res[1].subs(subs)) + + except StopIteration: + break + except TimeoutError: + num_args_list.append(args_list[i]) + i += 1 + + future = pool.map(_num_apply, num_args_list) + results = future.result() + + while True: + try: + res = next(results) + result_list.append(res[1]) + + except StopIteration: + break + + return result_list if __name__ == '__main__': from qgs.params.params import QgParams diff --git a/qgs/params/params.py b/qgs/params/params.py index 16feb91..35d2eaf 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -855,6 +855,7 @@ class QgParams(Params): 'L': sy.Symbol('L'), 'fo': sy.Symbol('f0'), 'beta': sy.Symbol('beta'), + 'n': sy.Symbol('n', positive=True), # Atmosphere Parameters 'kd': sy.Symbol('k_d'), diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 0370caa..d74df52 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -63,6 +63,8 @@ def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_p self.tensor = None self.jacobian_tensor = None + self.params.symbolic_insolation_array() + # self.compute_tensor() def _psi_a(self, i): @@ -141,6 +143,8 @@ def _deltaT_g(self, i): """ return i + self.params.variables_range[1] + 1 + #//TODO: Im not happy with having these set of properties in two places, one for numerics and one for symbolic. This should be combined, or at least put in the parameter section somewhere. + @property def sig0(self): return self.sym_params['sigma'] / 2 @@ -155,10 +159,55 @@ def G(self): @property def Cpgo(self): - return self.sym_params['gnd_C'] / (self.sym_params['gamma_g'] * self.sym_params['fo']) * self.sym_params['rr'] / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) + return self.sym_params['gnd_C'] / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) * self.params.rr / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) - #//TODO: Finish the rest of the constant functions - + @property + def Lpgo(self): + return self.sym_params['hlambda'] / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + + @property + def Cpa(self): + return self.sym_params['atm_C'] / (self.sym_params['atm_gamma'] * self.sym_params['fo']) * self.params.rr / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) / 2 + + @property + def Lpa(self): + return self.sym_params['hlambda'] / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + + #//TODO: Do we want to keep everthing symbolic? Including the Stefan Bolzmann const? + + @property + def sbpgo(self): + return 4 * self.params.sb * self.sym_params['gnd_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + + @property + def sbpa(self): + return 8 * self.sym_params['eps'] * self.param.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + + @property + def LSBpgo(self): + return 2 * self.sym_params['eps'] * self.params.sb * self.sym_params['gnd_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + + @property + def LSBpa(self): + return 8 * self.sym_params['eps'] * self.params.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + + @property + def T4sbpgo(self): + return self.params.sb * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['gnd_gamma'] * self.params.rr ** 3) + + @property + def T4sbpa(self): + return 16 * self.sym_params['eps'] * self.params.sb * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['gnd_gamma'] * self.params.rr ** 3) + + @property + def T4LSBpgo(self): + return 0.5 * self.sym_params['eps'] * self.params.sb * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['atm_gamma'] * self.params.rr ** 3) + + @property + def T4LSBpa(self): + return 16 * self.sym_params['eps'] * self.params.sb * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['atm_gamma'] * self.params.rr ** 3) + + #//TODO: Do i need the scaling parameters? def _compute_tensor_dicts(self): @@ -200,45 +249,48 @@ def _compute_tensor_dicts(self): # constructing some derived matrices if aips is not None: - a_inv = np.zeros((nvar[0], nvar[0])) + a_inv = {} for i in range(offset, nvar[1]): for j in range(offset, nvar[1]): - a_inv[i - offset, j - offset] = aips.a(i, j) + a_inv[(i - offset, j - offset)] = aips.a(i, j) - a_inv = np.linalg.inv(a_inv) - a_inv = sp.COO(a_inv) + a_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[0], nvar[0], a_inv) + a_inv = sy.matrices.Inverse(a_inv) - a_theta = np.zeros((nvar[1], nvar[1])) + a_theta = {} for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[i, j] = ap.sig0 * aips.a(i, j) - aips.u(i, j) - a_theta = np.linalg.inv(a_theta) - a_theta = sp.COO(a_theta) + a_theta[(i, j)] = ap.sig0 * aips.a(i, j) - aips.u(i, j) + + a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) + a_theta = sy.matrices.Inverse(a_theta) if bips is not None: if ocean: - U_inv = np.zeros((nvar[3], nvar[3])) + U_inv = {} for i in range(nvar[3]): for j in range(nvar[3]): - U_inv[i, j] = bips.U(i, j) - U_inv = np.linalg.inv(U_inv) - U_inv = sp.COO(U_inv) + U_inv[(i, j)] = bips.U(i, j) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) + U_inv = sy.matrices.Inverse(U_inv) - M_psio = np.zeros((nvar[2], nvar[2])) + M_psio = {} for i in range(offset, nvar[3]): for j in range(offset, nvar[3]): - M_psio[i - offset, j - offset] = bips.M(i, j) + par.G * bips.U(i, j) - M_psio = np.linalg.inv(M_psio) - M_psio = sp.COO(M_psio) + M_psio[(i - offset, j - offset)] = bips.M(i, j) + par.G * bips.U(i, j) + + M_psio = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) + M_psio = sy.matrices.Inverse(M_psio) + else: - U_inv = np.zeros((nvar[2], nvar[2])) + U_inv = {} for i in range(nvar[2]): for j in range(nvar[2]): - U_inv[i, j] = bips.U(i, j) - U_inv = np.linalg.inv(U_inv) - U_inv = sp.COO(U_inv) + U_inv[(i, j)] = bips.U(i, j) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(U_inv) - ################# + ################ if bips is not None: go = bips.stored @@ -250,39 +302,40 @@ def _compute_tensor_dicts(self): if aips.stored and go: # psi_a part for i in range(nvar[0]): - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') + t = {} #sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - val = a_inv[i, :] @ aips._c[offset:, jo] - t[self._psi_a(j), 0] -= val * scp.beta + val = a_inv[i, :] * aips._c[offset:, jo] + t[(self._psi_a(j), 0)] -= val * scp.beta - t[self._psi_a(j), 0] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 - t[self._theta_a(jo), 0] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 + t[(self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 + t[(self._theta_a(jo), 0)] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 if gp is not None: + # convert if gp.hk is not None: #//TODO: Need to make this symbolic if gp.orographic_basis == "atmospheric": - oro = a_inv[i, :] @ aips._g[offset:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect + oro = a_inv[i, :] * aips._g[offset:, jo, offset:] * self.sym_params['hk'] # not perfect else: #//TODO: Need to make this symbolic # TODO: Can only be used with symbolic inner products here - a warning or an error should be raised if this is not the case. - oro = a_inv[i, :] @ aips._gh[offset:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect - t[self._psi_a(j), 0] -= oro / 2 - t[self._theta_a(jo), 0] += oro / 2 + oro = a_inv[i, :] * aips._gh[offset:, jo, offset:] * self.sym_params['hk'] # not perfect + t[(self._psi_a(j), 0)] -= oro / 2 + t[(self._theta_a(jo), 0)] += oro / 2 for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists - val = a_inv[i, :] @ aips._b[offset:, jo, ko] + val = a_inv[i, :] * aips._b[offset:, jo, ko] t[self._psi_a(j), self._psi_a(k)] = - val t[self._theta_a(jo), self._theta_a(ko)] = - val if ocean: for j in range(nvar[2]): jo = j + offset # skipping the theta 0 variable if it exists - val = a_inv[i, :] @ aips._d[offset:, jo] + val = a_inv[i, :] * aips._d[offset:, jo] t[self._psi_o(j), 0] += val * symbolic_params['kd'] / 2 sparse_arrays_dict[self._psi_a(i)] = t.to_coo() @@ -292,10 +345,10 @@ def _compute_tensor_dicts(self): t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') #//TODO: Need to make this symbolic if par.Cpa is not None: - t[0, 0] -= a_theta[i, :] @ aips._u @ sp.COO(par.Cpa.astype(float)) # not perfect + t[0, 0] -= a_theta[i, :] * aips._u * self.Cpa # not perfect #//TODO: Need to make this symbolic if atp.hd is not None and atp.thetas is not None: - val = - a_theta[i, :] @ aips._u @ sp.COO(atp.thetas.astype(float)) # not perfect + val = - a_theta[i, :] * aips._u * sp.COO(atp.thetas.astype(float)) # not perfect t[0, 0] += val * symbolic_params['hd'] for j in range(nvar[0]): @@ -303,11 +356,11 @@ def _compute_tensor_dicts(self): jo = j + offset # skipping the theta 0 variable if it exists val = a_theta[i, :] @ aips._a[:, jo] - t[self._psi_a(j), 0] += val * symbolic_params['kd'] * symbolic_params['sigma'] / 2 / 2 - t[self._theta_a(jo), 0] -= val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * symbolic_params['sigma'] / 2 + t[self._psi_a(j), 0] += val * symbolic_params['kd'] * self.sig0 / 2 + t[self._theta_a(jo), 0] -= val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 val = - a_theta[i, :] @ aips._c[:, jo] - t[self._theta_a(jo), 0] += val * symbolic_params['beta'] * symbolic_params['sigma'] / 2 + t[self._theta_a(jo), 0] += val * symbolic_params['beta'] * self.sig0 if gp is not None: if gp.hk is not None: @@ -316,14 +369,14 @@ def _compute_tensor_dicts(self): oro = a_theta[i, :] @ aips._g[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect else: oro = a_theta[i, :] @ aips._gh[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect - t[self._theta_a(jo), 0] -= symbolic_params['sigma'] / 2 * oro / 2 - t[self._psi_a(j), 0] += symbolic_params['sigma'] / 2 * oro / 2 + t[self._theta_a(jo), 0] -= self.sig0 * oro / 2 + t[self._psi_a(j), 0] += self.sig0 * oro / 2 for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = a_theta[i, :] @ aips._b[:, jo, ko] - t[self._psi_a(j), self._theta_a(ko)] = - val * symbolic_params['sigma'] / 2 - t[self._theta_a(jo), self._psi_a(k)] = - val * symbolic_params['sigma'] / 2 + t[self._psi_a(j), self._theta_a(ko)] = - val * self.sig0 + t[self._theta_a(jo), self._psi_a(k)] = - val * self.sig0 val = a_theta[i, :] @ aips._g[:, jo, ko] t[self._psi_a(j), self._theta_a(ko)] += val @@ -342,22 +395,22 @@ def _compute_tensor_dicts(self): for j in range(nvar[2]): jo = j + offset # skipping the theta 0 variable if it exists val = - a_theta[i, :] @ aips._d[:, jo] - t[self._psi_o(j), 0] += val * ap.sig0 * ap.kd / 2 + t[self._psi_o(j), 0] += val * self.sig0 * ap.kd / 2 if par.Lpa is not None: for j in range(nvar[3]): val = - a_theta[i, :] @ aips._s[:, j] - t[self._deltaT_o(j), 0] += val * par.Lpa / 2 + t[self._deltaT_o(j), 0] += val * self.Lpa / 2 if par.LSBpgo is not None: - t[self._deltaT_o(j), 0] += val * par.LSBpgo + t[self._deltaT_o(j), 0] += val * self.LSBpgo if ground_temp: if par.Lpa is not None: for j in range(nvar[2]): val = - a_theta[i, :] @ aips._s[:, j] - t[self._deltaT_g(j), 0] += val * par.Lpa / 2 + t[self._deltaT_g(j), 0] += val * self.Lpa / 2 if par.LSBpgo is not None: - t[self._deltaT_g(j), 0] += val * par.LSBpgo + t[self._deltaT_g(j), 0] += val * self.LSBpgo sparse_arrays_dict[self._theta_a(i)] = t.to_coo() From eec1dd834e260d76a36be28ffef47ab70db56332 Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 10 Jul 2023 10:49:28 +0200 Subject: [PATCH 004/143] Ocean symbolic ip updated --- qgs/inner_products/symbolic.py | 85 ++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 20ea0d8..7561a23 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -36,7 +36,7 @@ # TODO: - Add warnings if trying to connect analytic and symbolic inner products together -_n = sy.Symbol('n', positive=True) +# _n = sy.Symbol('n', positive=True) class AtmosphericSymbolicInnerProducts(AtmosphericInnerProducts): @@ -690,7 +690,7 @@ class OceanicSymbolicInnerProducts(OceanicInnerProducts): symbolic computation. """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, symbolic_returned=False): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False): OceanicInnerProducts.__init__(self) @@ -740,7 +740,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.atmospheric_basis = None self.connected_to_atmosphere = False - self.subs = [(_n, self.n)] + self.return_symbolic = return_symbolic + if return_symbolic: + self._p_compute = _symbolic_compute + self.subs = [('n', params.symbolic_params['n'])] + else: + self._p_compute = _parallel_compute + self.subs = [('n', self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() @@ -807,31 +813,42 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None args_list = [[(i, j), self.iip.ip_lap, (self._phi(i), self._F(j))] for i in range(self.noc) for j in range(natm)] - _parallel_compute(pool, args_list, subs, self._K, timeout) + output = self._p_compute(pool, args_list, subs, self._K, timeout) + + if self.return_symbolic: + self._K = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, natm, output) + else: + self._K = self._K.to_coo() # W inner products args_list = [[(i, j), self.iip.symbolic_inner_product, (self._phi(i), self._F(j))] for i in range(self.noc) for j in range(natm)] - _parallel_compute(pool, args_list, subs, self._W, timeout) + output = self._p_compute(pool, args_list, subs, self._W, timeout) + + if self.return_symbolic: + self._W = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, natm, output) + else: + self._W = self._W.to_coo() if self._T4: # Z inner products args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.noc) for j in range(natm) for k in range(j, natm) for ell in range(k, natm) for m in range(ell, natm)] - _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) elif self._dynamic_T: # Z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.noc) for m in range(natm)] - _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True) - self._K = self._K.to_coo() - self._W = self._W.to_coo() if self._T4 or self._dynamic_T: - self._Z = self._Z.to_coo() + if self.return_symbolic: + self._Z = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, natm, natm, natm, natm)) + else: + self._Z = self._Z.to_coo() def compute_inner_products(self, num_threads=None, timeout=None): """Function computing and storing all the inner products at once. @@ -867,50 +884,68 @@ def compute_inner_products(self, num_threads=None, timeout=None): # N inner products args_list = [[(i, j), self.ip.ip_diff_x, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - _parallel_compute(pool, args_list, subs, self._N, timeout) + output = self._p_compute(pool, args_list, subs, self._N, timeout) + if self.return_symbolic: + self._N = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + else: + self._N = self._N.to_coo() # M inner products args_list = [[(i, j), self.ip.ip_lap, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - _parallel_compute(pool, args_list, subs, self._M, timeout) + output = self._p_compute(pool, args_list, subs, self._M, timeout) + if self.return_symbolic: + self._M = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + else: + self._M = self._M.to_coo() # U inner products args_list = [[(i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - _parallel_compute(pool, args_list, subs, self._U, timeout) + output = self._p_compute(pool, args_list, subs, self._U, timeout) + if self.return_symbolic: + self._U = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + else: + self._U = self._U.to_coo() # O inner products args_list = [[(i, j, k), self.ip.ip_jac, (self._phi(i), self._phi(j), self._phi(k))] for i in range(self.noc) for j in range(self.noc) for k in range(self.noc)] - _parallel_compute(pool, args_list, subs, self._O, timeout) + output = self._p_compute(pool, args_list, subs, self._O, timeout) + if self.return_symbolic: + self._O = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + else: + self._O = self._O.to_coo() # C inner products args_list = [[(i, j, k), self.ip.ip_jac_lap, (self._phi(i), self._phi(j), self._phi(k))] for i in range(self.noc) for j in range(self.noc) for k in range(self.noc)] - _parallel_compute(pool, args_list, subs, self._C, timeout) + output = self._p_compute(pool, args_list, subs, self._C, timeout) + if self.return_symbolic: + self._C = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + else: + self._C = self._C.to_coo() if self._T4: # V inner products args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.noc) for j in range(self.noc) for k in range(j, self.noc) for ell in range(k, self.noc) for m in range(ell, self.noc)] - _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) elif self._dynamic_T: # V inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.noc) for m in range(self.noc)] - _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) - self._M = self._M.to_coo() - self._U = self._U.to_coo() - self._N = self._N.to_coo() - self._O = self._O.to_coo() - self._C = self._C.to_coo() - if self._T4 or self._dynamic_T: - self._V = self._V.to_coo() + if self._T4 or self._dynamic_T: + if self.return_symbolic: + self._V = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc, self.noc, self.noc)) + else: + self._V = self._V.to_coo() @property def noc(self): @@ -1434,6 +1469,8 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False else: num_args_list = [args + [subs] for args in args_list] + print(num_args_list) + print("\n") future = pool.map(_num_apply, num_args_list) results = future.result() if permute: From 0e8b1c70216560a34e72ebd408ad1a962502228d Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 10 Jul 2023 10:59:08 +0200 Subject: [PATCH 005/143] Ground symbolic ip updated --- qgs/inner_products/symbolic.py | 42 ++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 7561a23..5dbfd6a 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -1114,7 +1114,7 @@ class GroundSymbolicInnerProducts(GroundInnerProducts): """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, symbolic_returned=False): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False): GroundInnerProducts.__init__(self) @@ -1164,7 +1164,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.atmospheric_basis = None self.connected_to_atmosphere = False - self.subs = [(_n, self.n)] + self.return_symbolic = return_symbolic + if return_symbolic: + self._p_compute = _symbolic_compute + self.subs = [('n', params.symbolic_params['n'])] + else: + self._p_compute = _parallel_compute + self.subs = [('n', self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() @@ -1228,24 +1234,30 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None # W inner products args_list = [[(i, j), self.iip.symbolic_inner_product, (self._phi(i), self._F(j))] for i in range(self.ngr) for j in range(natm)] - _parallel_compute(pool, args_list, subs, self._W, timeout) + output = self._p_compute(pool, args_list, subs, self._W, timeout) + if self.return_symbolic: + self._W = sy.matrices.immutable.ImmutableSparseMatrix(self.ngr, natm, output) + else: + self._W = self._W.to_coo() if self._T4: # Z inner products args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.ngr) for j in range(natm) for k in range(j, natm) for ell in range(k, natm) for m in range(ell, natm)] - _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) elif self._dynamic_T: # Z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.ngr) for m in range(natm)] - _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) - self._W = self._W.to_coo() if self._T4 or self._dynamic_T: - self._Z = self._Z.to_coo() + if self.return_symbolic: + self._Z = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.ngr, natm, natm, natm, natm)) + else: + self._Z = self._Z.to_coo() def compute_inner_products(self, num_threads=None, timeout=None): """Function computing and storing all the inner products at once. @@ -1275,24 +1287,30 @@ def compute_inner_products(self, num_threads=None, timeout=None): # U inner products args_list = [[(i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j))] for i in range(self.ngr) for j in range(self.ngr)] - _parallel_compute(pool, args_list, subs, self._U, timeout) + output = self._p_compute(pool, args_list, subs, self._U, timeout) + if self.return_symbolic: + self._U = sy.matrices.immutable.ImmutableSparseMatrix(self.ngr, self.ngr, output) + else: + self._U = self._U.to_coo() if self._T4: # V inner products args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.ngr) for j in range(self.ngr) for k in range(j, self.ngr) for ell in range(k, self.ngr) for m in range(ell, self.ngr)] - _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) elif self._dynamic_T: # V inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.ngr) for m in range(self.ngr)] - _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) - self._U = self._U.to_coo() if self._T4 or self._dynamic_T: - self._V = self._V.to_coo() + if self.return_symbolic: + self._V = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.ngr, self.ngr, self.ngr, self.ngr, self.ngr)) + else: + self._V = self._V.to_coo() @property def ngr(self): From 7b0156493cb06c18f3b1e15da83dad70255b4bb8 Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 10 Jul 2023 13:05:10 +0200 Subject: [PATCH 006/143] working symbolic ip - not tested --- qgs/inner_products/symbolic.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 5dbfd6a..5b33dc5 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -842,7 +842,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.noc) for m in range(natm)] - output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -914,7 +914,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = self._p_compute(pool, args_list, subs, self._O, timeout) if self.return_symbolic: - self._O = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + self._O = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) else: self._O = self._O.to_coo() @@ -924,7 +924,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = self._p_compute(pool, args_list, subs, self._C, timeout) if self.return_symbolic: - self._C = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + self._C = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) else: self._C = self._C.to_coo() @@ -1487,8 +1487,6 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False else: num_args_list = [args + [subs] for args in args_list] - print(num_args_list) - print("\n") future = pool.map(_num_apply, num_args_list) results = future.result() if permute: From d554839880ddc8acdbb73972328f4b77dfbe2d68 Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 10 Jul 2023 17:25:12 +0200 Subject: [PATCH 007/143] Symbolic tensor DE started - not working --- qgs/inner_products/symbolic.py | 11 +- qgs/tensors/symbolic_qgtensor.py | 574 +++++++++++++++++++++++-------- 2 files changed, 438 insertions(+), 147 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 5b33dc5..8f1a6ae 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -408,8 +408,11 @@ def compute_inner_products(self, num_threads=None, timeout=None): with Pool(max_workers=num_threads) as pool: #//TODO: Fix substitution here, temporary fix - subs = self.subs #+ self.atmospheric_basis.substitutions - + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions + # a inner products args_list = [[(i, j), self.ip.ip_lap, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] @@ -800,7 +803,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - + #//TODO: Check if this needs to be edited for symbolic ip subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions natm = len(atmosphere_basis) @@ -1223,7 +1226,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - + #//TODO: Check if this needs changing for symbolic IP. subs = self.subs + self.ground_basis.substitutions + self.atmospheric_basis.substitutions natm = len(atmosphere_basis) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index d74df52..c0bcfcf 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -12,7 +12,7 @@ import sympy as sy import pickle -class SymbolicLinTensor(object): +class SymbolicTensorLinear(object): """Symbolic qgs tendencies tensor class. Parameters @@ -249,7 +249,7 @@ def _compute_tensor_dicts(self): # constructing some derived matrices if aips is not None: - a_inv = {} + a_inv = dict() for i in range(offset, nvar[1]): for j in range(offset, nvar[1]): a_inv[(i - offset, j - offset)] = aips.a(i, j) @@ -257,7 +257,7 @@ def _compute_tensor_dicts(self): a_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[0], nvar[0], a_inv) a_inv = sy.matrices.Inverse(a_inv) - a_theta = {} + a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): a_theta[(i, j)] = ap.sig0 * aips.a(i, j) - aips.u(i, j) @@ -267,14 +267,14 @@ def _compute_tensor_dicts(self): if bips is not None: if ocean: - U_inv = {} + U_inv = dict() for i in range(nvar[3]): for j in range(nvar[3]): U_inv[(i, j)] = bips.U(i, j) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) U_inv = sy.matrices.Inverse(U_inv) - M_psio = {} + M_psio = dict() for i in range(offset, nvar[3]): for j in range(offset, nvar[3]): M_psio[(i - offset, j - offset)] = bips.M(i, j) + par.G * bips.U(i, j) @@ -283,7 +283,7 @@ def _compute_tensor_dicts(self): M_psio = sy.matrices.Inverse(M_psio) else: - U_inv = {} + U_inv = dict() for i in range(nvar[2]): for j in range(nvar[2]): U_inv[(i, j)] = bips.U(i, j) @@ -297,22 +297,20 @@ def _compute_tensor_dicts(self): else: go = True - sparse_arrays_dict = dict() + symbolic_array_dic = dict() if aips.stored and go: # psi_a part for i in range(nvar[0]): - t = {} #sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') - for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists val = a_inv[i, :] * aips._c[offset:, jo] - t[(self._psi_a(j), 0)] -= val * scp.beta + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= val * scp.beta - t[(self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 - t[(self._theta_a(jo), 0)] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 + symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 if gp is not None: # convert @@ -324,43 +322,39 @@ def _compute_tensor_dicts(self): #//TODO: Need to make this symbolic # TODO: Can only be used with symbolic inner products here - a warning or an error should be raised if this is not the case. oro = a_inv[i, :] * aips._gh[offset:, jo, offset:] * self.sym_params['hk'] # not perfect - t[(self._psi_a(j), 0)] -= oro / 2 - t[(self._theta_a(jo), 0)] += oro / 2 + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= oro / 2 + symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] += oro / 2 for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = a_inv[i, :] * aips._b[offset:, jo, ko] - t[self._psi_a(j), self._psi_a(k)] = - val - t[self._theta_a(jo), self._theta_a(ko)] = - val + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), self._psi_a(k))] = - val + symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), self._theta_a(ko))] = - val if ocean: for j in range(nvar[2]): jo = j + offset # skipping the theta 0 variable if it exists val = a_inv[i, :] * aips._d[offset:, jo] - t[self._psi_o(j), 0] += val * symbolic_params['kd'] / 2 - - sparse_arrays_dict[self._psi_a(i)] = t.to_coo() + symbolic_array_dic[(self._psi_a(i), self._psi_o(j), 0)] += val * symbolic_params['kd'] / 2 # theta_a part for i in range(nvar[1]): - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') - #//TODO: Need to make this symbolic if par.Cpa is not None: - t[0, 0] -= a_theta[i, :] * aips._u * self.Cpa # not perfect - #//TODO: Need to make this symbolic + symbolic_array_dic[(self._theta_a(i), 0, 0)] -= a_theta[i, :] * aips._u * self.Cpa # not perfect + if atp.hd is not None and atp.thetas is not None: val = - a_theta[i, :] * aips._u * sp.COO(atp.thetas.astype(float)) # not perfect - t[0, 0] += val * symbolic_params['hd'] + symbolic_array_dic[(self._theta_a(i), 0, 0)] += val * symbolic_params['hd'] for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists val = a_theta[i, :] @ aips._a[:, jo] - t[self._psi_a(j), 0] += val * symbolic_params['kd'] * self.sig0 / 2 - t[self._theta_a(jo), 0] -= val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += val * symbolic_params['kd'] * self.sig0 / 2 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 val = - a_theta[i, :] @ aips._c[:, jo] - t[self._theta_a(jo), 0] += val * symbolic_params['beta'] * self.sig0 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] += val * symbolic_params['beta'] * self.sig0 if gp is not None: if gp.hk is not None: @@ -369,127 +363,111 @@ def _compute_tensor_dicts(self): oro = a_theta[i, :] @ aips._g[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect else: oro = a_theta[i, :] @ aips._gh[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect - t[self._theta_a(jo), 0] -= self.sig0 * oro / 2 - t[self._psi_a(j), 0] += self.sig0 * oro / 2 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= self.sig0 * oro / 2 + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += self.sig0 * oro / 2 for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = a_theta[i, :] @ aips._b[:, jo, ko] - t[self._psi_a(j), self._theta_a(ko)] = - val * self.sig0 - t[self._theta_a(jo), self._psi_a(k)] = - val * self.sig0 + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), self._theta_a(ko))] = - val * self.sig0 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), self._psi_a(k))] = - val * self.sig0 val = a_theta[i, :] @ aips._g[:, jo, ko] - t[self._psi_a(j), self._theta_a(ko)] += val + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), self._theta_a(ko))] += val for j in range(nvar[1]): val = a_theta[i, :] @ aips._u[:, j] if par.Lpa is not None: - t[self._theta_a(j), 0] += val * atp.sc * par.Lpa + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.sym_params['sc'] * self.Lpa if par.LSBpa is not None: - t[self._theta_a(j), 0] += val * par.LSBpa + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.LSBpa if atp.hd is not None: - t[self._theta_a(j), 0] += val * atp.hd + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * atp.hd if ocean: for j in range(nvar[2]): jo = j + offset # skipping the theta 0 variable if it exists val = - a_theta[i, :] @ aips._d[:, jo] - t[self._psi_o(j), 0] += val * self.sig0 * ap.kd / 2 + symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * self.sym_params['kd'] / 2 if par.Lpa is not None: for j in range(nvar[3]): val = - a_theta[i, :] @ aips._s[:, j] - t[self._deltaT_o(j), 0] += val * self.Lpa / 2 + symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.Lpa / 2 if par.LSBpgo is not None: - t[self._deltaT_o(j), 0] += val * self.LSBpgo + symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.LSBpgo if ground_temp: if par.Lpa is not None: for j in range(nvar[2]): val = - a_theta[i, :] @ aips._s[:, j] - t[self._deltaT_g(j), 0] += val * self.Lpa / 2 + symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.Lpa / 2 if par.LSBpgo is not None: - t[self._deltaT_g(j), 0] += val * self.LSBpgo - - sparse_arrays_dict[self._theta_a(i)] = t.to_coo() + symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.LSBpgo if ocean: # psi_o part for i in range(nvar[2]): - - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') - for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - val = M_psio[i, :] @ bips._K[offset:, jo] * op.d - t[self._psi_a(j), 0] += val - t[self._theta_a(jo), 0] -= val + val = M_psio[i, :] @ bips._K[offset:, jo] * self.sym_params['d'] + symbolic_array_dic[(self._psi_o(i), self._psi_a(j), 0)] += val + symbolic_array_dic[(self._psi_o(i), self._theta_a(jo), 0)] -= val for j in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists val = - M_psio[i, :] @ bips._N[offset:, jo] - t[self._psi_o(j), 0] += val * scp.beta + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * self.sym_params['beta'] val = - M_psio[i, :] @ bips._M[offset:, jo] - t[self._psi_o(j), 0] += val * (op.r + op.d) + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * (self.sym_params['r'] + self.sym_params['d']) for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists - t[self._psi_o(j), self._psi_o(k)] -= M_psio[i, :] @ bips._C[offset:, jo, ko] - - sparse_arrays_dict[self._psi_o(i)] = t.to_coo() + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), self._psi_o(k))] -= M_psio[i, :] @ bips._C[offset:, jo, ko] # deltaT_o part for i in range(nvar[3]): - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') - t[0, 0] += U_inv[i, :] @ bips._W @ sp.COO(par.Cpgo.astype(float)) + symbolic_array_dic[(self._deltaT_o(i), 0, 0)] += U_inv[i, :] @ bips._W @ sp.COO(self.Cpgo) for j in range(nvar[1]): val = U_inv[i, :] @ bips._W[:, j] - t[self._theta_a(j), 0] += val * 2 * atp.sc * par.Lpgo + symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * self.sym_params['sc'] * self.Lpgo if par.sbpa is not None: - t[self._theta_a(j), 0] += val * par.sbpa + symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * self.sbpa for j in range(nvar[3]): - t[self._deltaT_o(j), 0] = - par.Lpgo * _kronecker_delta(i, j) + symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) if par.sbpgo is not None: - t[self._deltaT_o(j), 0] += - par.sbpgo * _kronecker_delta(i, j) + symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) for j in range(nvar[2]): for k in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists ko = k + offset # skipping the T 0 variable if it exists - t[self._psi_o(j), self._deltaT_o(ko)] -= U_inv[i, :] @ bips._O[:, jo, ko] - - sparse_arrays_dict[self._deltaT_o(i)] = t.to_coo() + symbolic_array_dic[(self._deltaT_o(i), self._psi_o(j), self._deltaT_o(ko))] -= U_inv[i, :] @ bips._O[:, jo, ko] # deltaT_g part if ground_temp: for i in range(nvar[2]): - - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') - t[0, 0] += U_inv[i, :] @ bips._W @ sp.COO(par.Cpgo.astype(float)) # not perfect + symbolic_array_dic[(self._deltaT_g(i), 0, 0)] += U_inv[i, :] @ bips._W @ sp.COO(self.Cpgo) # not perfect for j in range(nvar[1]): val = U_inv[i, :] @ bips._W[:, j] - t[self._theta_a(j), 0] += val * 2 * atp.sc * par.Lpgo - if par.sbpa is not None: - t[self._theta_a(j), 0] += val * par.sbpa + symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * self.sym_params['sc'] * self.Lpgo + if self.sbpa is not None: + symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * self.sbpa for j in range(nvar[2]): - t[self._deltaT_g(j), 0] = - par.Lpgo * _kronecker_delta(i, j) - if par.sbpgo is not None: - t[self._deltaT_g(j), 0] += - par.sbpgo * _kronecker_delta(i, j) - - sparse_arrays_dict[self._deltaT_g(i)] = t.to_coo() + symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) + if self.sbpgo is not None: + symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] += - par.sbpgo * _kronecker_delta(i, j) else: # psi_a part for i in range(nvar[0]): - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') - for j in range(nvar[0]): jo = j + offset @@ -497,11 +475,12 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.c(offset + jj, jo) - t[self._psi_a(j), 0] -= val * scp.beta + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= val * self.sym_params['beta'] - t[self._psi_a(j), 0] -= (ap.kd * _kronecker_delta(i, j)) / 2 - t[self._theta_a(jo), 0] = (ap.kd * _kronecker_delta(i, j)) / 2 + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (ap.kd * _kronecker_delta(i, j)) / 2 + symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] = (ap.kd * _kronecker_delta(i, j)) / 2 + #//TODO: what is gp.hk parameter??? if gp is not None: if gp.hk is not None: oro = 0 @@ -513,41 +492,38 @@ def _compute_tensor_dicts(self): for jj in range(nvar[0]): for kk in range(nvar[0]): oro += a_inv[i, jj] * aips.gh(offset + jj, j, offset + kk) * gp.hk[kk] - t[self._psi_a(j), 0] -= oro / 2 - t[self._theta_a(jo), 0] += oro / 2 + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= oro / 2 + symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] += oro / 2 for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.b(offset + jj, jo, ko) - t[self._psi_a(j), self._psi_a(k)] = - val - t[self._theta_a(jo), self._theta_a(ko)] = - val + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), self._psi_a(k))] = - val + symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), self._theta_a(ko))] = - val if ocean: for j in range(nvar[2]): jo = j + offset val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.d(offset + jj, jo) - t[self._psi_o(j), 0] += val * ap.kd / 2 + symbolic_array_dic[(self._psi_a(i), self._psi_o(j), 0)] += val * ap.kd / 2 - sparse_arrays_dict[self._psi_a(i)] = t.to_coo() # theta_a part for i in range(nvar[1]): - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') - if par.Cpa is not None: for jj in range(nvar[1]): for kk in range(nvar[1]): - t[0, 0] -= a_theta[i, jj] * aips.u(jj, kk) * par.Cpa[kk] + symbolic_array_dic[(self._theta_a(i), 0, 0)] -= a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk] - if atp.hd is not None and atp.thetas is not None: + if atp.hd is not None and self.thetas is not None: val = 0 for jj in range(nvar[1]): for kk in range(nvar[1]): - val -= a_theta[i, jj] * aips.u(jj, kk) * atp.thetas[kk] - t[0, 0] += val * atp.hd + val -= a_theta[i, jj] * aips.u(jj, kk) * self.thetas[kk] + symbolic_array_dic[(self._theta_a(i), 0, 0)] += val * atp.hd for j in range(nvar[0]): @@ -556,13 +532,13 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.a(jj, jo) - t[self._psi_a(j), 0] += val * ap.kd * ap.sig0 / 2 - t[self._theta_a(jo), 0] -= val * (ap.kd / 2 + 2 * ap.kdp) * ap.sig0 + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += val * self.sym_params['kd'] * self.sig0 / 2 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= val * (self.sym_params['kd'] / 2 + 2 * self.sym_params['kdp']) * self.sig0 val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.c(jj, jo) - t[self._theta_a(jo), 0] += val * scp.beta * ap.sig0 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] += val * self.sym_params['beta'] * self.sig0 if gp is not None: if gp.hk is not None: @@ -570,27 +546,27 @@ def _compute_tensor_dicts(self): if gp.orographic_basis == "atmospheric": for jj in range(nvar[1]): for kk in range(nvar[0]): - oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * gp.hk[kk] + oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * self.hk[kk] else: for jj in range(nvar[1]): for kk in range(nvar[0]): - oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * gp.hk[kk] - t[self._theta_a(jo), 0] -= ap.sig0 * oro / 2 - t[self._psi_a(j), 0] += ap.sig0 * oro / 2 + oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * self.hk[kk] + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= self.sig0 * oro / 2 + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += self.sig0 * oro / 2 for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.b(jj, jo, ko) - t[self._psi_a(j), self._theta_a(ko)] = - val * ap.sig0 - t[self._theta_a(jo), self._psi_a(k)] = - val * ap.sig0 + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), self._theta_a(ko))] = - val * self.sig0 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), self._psi_a(k))] = - val * self.sig0 val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.g(jj, jo, ko) - t[self._psi_a(j), self._theta_a(ko)] += val + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), self._theta_a(ko))] += val for j in range(nvar[1]): val = 0 @@ -598,12 +574,12 @@ def _compute_tensor_dicts(self): val += a_theta[i, jj] * aips.u(jj, j) if par.Lpa is not None: - t[self._theta_a(j), 0] += val * atp.sc * par.Lpa + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.sym_params['sc'] * self.Lpa if par.LSBpa is not None: - t[self._theta_a(j), 0] += val * par.LSBpa + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.LSBpa if atp.hd is not None: - t[self._theta_a(j), 0] += val * atp.hd + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * atp.hd if ocean: for j in range(nvar[2]): @@ -611,42 +587,37 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.d(jj, jo) - t[self._psi_o(j), 0] += val * ap.sig0 * ap.kd / 2 + symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * self.sym_params['kd'] / 2 if par.Lpa is not None: for j in range(nvar[3]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) - t[self._deltaT_o(j), 0] += val * par.Lpa / 2 + symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.Lpa / 2 if par.LSBpgo is not None: - t[self._deltaT_o(j), 0] += val * par.LSBpgo + symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.LSBpgo if ground_temp: - if par.Lpa is not None: + if self.Lpa is not None: for j in range(nvar[2]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) - t[self._deltaT_g(j), 0] += val * par.Lpa / 2 - if par.LSBpgo is not None: - t[self._deltaT_g(j), 0] += val * par.LSBpgo - - sparse_arrays_dict[self._theta_a(i)] = t.to_coo() + symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.Lpa / 2 + if self.LSBpgo is not None: + symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.LSBpgo if ocean: # psi_o part for i in range(nvar[2]): - - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') - for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists for jj in range(nvar[2]): - val = M_psio[i, jj] * bips.K(offset + jj, jo) * op.d - t[self._psi_a(j), 0] += val - t[self._theta_a(jo), 0] -= val + val = M_psio[i, jj] * bips.K(offset + jj, jo) * self.sym_params['d'] + symbolic_array_dic[(self._psi_o(i), self._psi_a(j), 0)] += val + symbolic_array_dic[(self._psi_o(i), self._theta_a(jo), 0)] -= val for j in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists @@ -654,74 +625,64 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.N(offset + jj, jo) - t[self._psi_o(j), 0] += val * scp.beta + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * self.sym_params['beta'] val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.M(offset + jj, jo) - t[self._psi_o(j), 0] += val * (op.r + op.d) + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * (self.sym_params['r'] + self.sym_params['d']) for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists for jj in range(nvar[2]): - t[self._psi_o(j), self._psi_o(k)] -= M_psio[i, jj] * bips.C(offset + jj, jo, ko) - - sparse_arrays_dict[self._psi_o(i)] = t.to_coo() + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), self._psi_o(k))] -= M_psio[i, jj] * bips.C(offset + jj, jo, ko) # deltaT_o part for i in range(nvar[3]): - - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') for jj in range(nvar[1]): for kk in range(nvar[3]): - t[0, 0] += U_inv[i, kk] * bips.W(kk, jj) * par.Cpgo[jj] + symbolic_array_dic[(self._deltaT_o(i), 0, 0)] += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] for j in range(nvar[1]): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips.W(jj, j) - t[self._theta_a(j), 0] += val * 2 * atp.sc * par.Lpgo + symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * self.sym_params['sc'] * self.Lpgo if par.sbpa is not None: - t[self._theta_a(j), 0] += val * par.sbpa + symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * self.sbpa for j in range(nvar[3]): - t[self._deltaT_o(j), 0] = - par.Lpgo * _kronecker_delta(i, j) + symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) if par.sbpgo is not None: - t[self._deltaT_o(j), 0] += - par.sbpgo * _kronecker_delta(i, j) + symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) for j in range(nvar[2]): for k in range(offset, nvar[3]): jo = j + offset # skipping the T 0 variable if it exists for jj in range(nvar[3]): - t[self._psi_o(j), self._deltaT_o(k)] -= U_inv[i, jj] * bips.O(jj, jo, k) - - sparse_arrays_dict[self._deltaT_o(i)] = t.to_coo() + symbolic_array_dic[(self._deltaT_o(i), self._psi_o(j), self._deltaT_o(k))] -= U_inv[i, jj] * bips.O(jj, jo, k) # deltaT_g part if ground_temp: for i in range(nvar[2]): - - t = sp.zeros((ndim + 1, ndim + 1), dtype=np.float64, format='dok') for jj in range(nvar[1]): for kk in range(nvar[2]): - t[0, 0] += U_inv[i, kk] * bips.W(kk, jj) * par.Cpgo[jj] + symbolic_array_dic[(self._deltaT_g(i), 0, 0)] += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] for j in range(nvar[1]): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips.W(jj, j) - t[self._theta_a(j), 0] += val * 2 * atp.sc * par.Lpgo + symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * self.sym_params['sc'] * self.Lpgo if par.sbpa is not None: - t[self._theta_a(j), 0] += val * par.sbpa + symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * self.sbpa for j in range(nvar[2]): - t[self._deltaT_g(j), 0] = - par.Lpgo * _kronecker_delta(i, j) + symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) if par.sbpgo is not None: - t[self._deltaT_g(j), 0] += - par.sbpgo * _kronecker_delta(i, j) + symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) - sparse_arrays_dict[self._deltaT_g(i)] = t.to_coo() - - return sparse_arrays_dict + return symbolic_array_dic def compute_tensor(self): """Routine to compute the tensor.""" @@ -742,6 +703,333 @@ def _set_tensor(self, tensor): self.jacobian_tensor = self.jacobian_from_tensor(tensor) self.tensor = self.simplify_tensor(tensor) + #//TODO: include other functions for altering dictionary. + +class SymbolicTensorDynamicT(SymbolicTensorLinear): + #//TODO: Need to work out symbolic tensor dot + + """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. + + Parameters + ---------- + params: None or QgParams, optional + The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. + atmospheric_inner_products: None or AtmosphericInnerProducts, optional + The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. + If None, disable the atmospheric tendencies. Default to `None`. + oceanic_inner_products: None or OceanicInnerProducts, optional + The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. + If None, disable the oceanic tendencies. Default to `None`. + ground_inner_products: None or GroundInnerProducts, optional + The inner products of the ground basis functions on which the model's PDE ground equations are projected. + If None, disable the ground tendencies. Default to `None`. + + Attributes + ---------- + params: None or QgParams + The models parameters used to configure the tensor. `None` for an empty tensor. + atmospheric_inner_products: None or AtmosphericInnerProducts + The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. + If None, disable the atmospheric tendencies. Default to `None`. + oceanic_inner_products: None or OceanicInnerProducts + The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. + If None, disable the oceanic tendencies. Default to `None`. + ground_inner_products: None or GroundInnerProducts + The inner products of the ground basis functions on which the model's PDE ground equations are projected. + If None, disable the ground tendencies. Default to `None`. + tensor: sparse.COO(float) + The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. + jacobian_tensor: sparse.COO(float) + The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. + """ + + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): + + SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) + + def _compute_tensor_dicts(self): + + if self.params is None: + return None + + if self.atmospheric_inner_products is None and self.oceanic_inner_products is None \ + and self.ground_inner_products is None: + return None + + aips = self.atmospheric_inner_products + + bips = None + if self.oceanic_inner_products is not None: + bips = self.oceanic_inner_products + + elif self.ground_inner_products is not None: + bips = self.ground_inner_products + + if bips is not None: + go = bips.stored + else: + go = True + + if aips.stored and go: + symbolic_array_full_dict = self._compute_stored_full_dict() + + else: + symbolic_array_full_dict = self._compute_non_stored_full_dict() + + return symbolic_array_full_dict + + def _compute_stored_full_dict(self): + par = self.params + nvar = par.number_of_variables + aips = self.atmospheric_inner_products + + bips = None + if self.oceanic_inner_products is not None: + bips = self.oceanic_inner_products + ocean = True + else: + ocean = False + + if self.ground_inner_products is not None: + bips = self.ground_inner_products + ground_temp = True + else: + ground_temp = False + + # constructing some derived matrices + if aips is not None: + + a_theta = dict() + for i in range(nvar[1]): + for j in range(nvar[1]): + a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) + a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) + a_theta = sy.matrices.Inverse(a_theta) + + if bips is not None: + U_inv = dict() + if ocean: + for i in range(nvar[3]): + for j in range(nvar[3]): + U_inv[(i, j)] = bips.U(i, j) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) + U_inv = sy.matrices.Inverse(U_inv) + else: + for i in range(nvar[2]): + for j in range(nvar[2]): + U_inv[i, j] = bips.U(i, j) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(U_inv) + + ################# + + symbolic_array_full_dict = dict() + # theta_a part + for i in range(nvar[1]): + # symbolic_array_full_dict[self._theta_a(i)] = list() + + if self.T4LSBpa is not None: + # val = sy.physics.quantum.tensorproduct.TensorProduct((a_theta[i], aips._z)) + # val = sp.tensordot(a_theta[i], aips._z, axes=1) + if val.nnz > 0: + symbolic_array_full_dict[self._theta_a(i)].append(self._shift_tensor_coordinates(self.T4LSBpa * val, self._theta_a(0))) + + if ocean: + if par.T4LSBpgo is not None: + val = sp.tensordot(a_theta[i], aips._v, axes=1) + if val.nnz > 0: + symbolic_array_full_dict[self._theta_a(i)].append(self._shift_tensor_coordinates(- self.T4LSBpgo * val, self._deltaT_o(0))) + + if ground_temp: + + if par.T4LSBpgo is not None: + val = sp.tensordot(a_theta[i], aips._v, axes=1) + if val.nnz > 0: + symbolic_array_full_dict[self._theta_a(i)].append(self._shift_tensor_coordinates(- self.T4LSBpgo * val, self._deltaT_g(0))) + + if ocean: + # deltaT_o part + for i in range(nvar[3]): + symbolic_array_full_dict[self._deltaT_o(i)] = list() + val = sp.tensordot(U_inv[i], bips._Z, axes=1) + if val.nnz > 0: + symbolic_array_full_dict[self._deltaT_o(i)].append(self._shift_tensor_coordinates(self.T4sbpa * val, self._theta_a(0))) + val = sp.tensordot(U_inv[i], bips._V, axes=1) + if val.nnz > 0: + symbolic_array_full_dict[self._deltaT_o(i)].append(self._shift_tensor_coordinates(- self.T4sbpgo * val, self._deltaT_o(0))) + + if ground_temp: + # deltaT_g part + for i in range(nvar[2]): + symbolic_array_full_dict[self._deltaT_g(i)] = list() + val = sp.tensordot(U_inv[i], bips._Z, axes=1) + if val.nnz > 0: + symbolic_array_full_dict[self._deltaT_g(i)].append(self._shift_tensor_coordinates(self.T4sbpa * val, self._theta_a(0))) + val = sp.tensordot(U_inv[i], bips._V, axes=1) + if val.nnz > 0: + symbolic_array_full_dict[self._deltaT_g(i)].append(self._shift_tensor_coordinates(- self.T4sbpgo * val, self._deltaT_g(0))) + + return symbolic_array_full_dict + + def _compute_non_stored_full_dict(self): + par = self.params + ap = par.atmospheric_params + nvar = par.number_of_variables + ndim = par.ndim + aips = self.atmospheric_inner_products + + bips = None + if self.oceanic_inner_products is not None: + bips = self.oceanic_inner_products + ocean = True + else: + ocean = False + + if self.ground_inner_products is not None: + bips = self.ground_inner_products + ground_temp = True + else: + ground_temp = False + + # constructing some derived matrices + if aips is not None: + + a_theta = np.zeros((nvar[1], nvar[1])) + for i in range(nvar[1]): + for j in range(nvar[1]): + a_theta[i, j] = self.sig0 * aips.a(i, j) - aips.u(i, j) + a_theta = np.linalg.inv(a_theta) + a_theta = sp.COO(a_theta) + + if bips is not None: + if ocean: + U_inv = dict() + for i in range(nvar[3]): + for j in range(nvar[3]): + U_inv[i, j] = bips.U(i, j) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) + + else: + U_inv = dict() + for i in range(nvar[2]): + for j in range(nvar[2]): + U_inv[(i, j)] = bips.U(i, j) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) + + U_inv = sy.matrices.Inverse(U_inv) + + + ################# + + symbolic_array_full_dict = dict() + # theta_a part + for i in range(nvar[1]): + # t_full = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='dok') + + if par.T4LSBpa is not None: + j = k = ell = 0 + for m in range(nvar[1]): + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips.z(jj, j, k, ell, m) + if m == 0: + symbolic_array_full_dict[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4LSBpa * val + else: + symbolic_array_full_dict[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4LSBpa * val + + if ocean: + if par.T4LSBpgo is not None: + j = k = ell = 0 + for m in range(nvar[3]): + val = 0 + for jj in range(nvar[1]): + val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) + if m == 0: + symbolic_array_full_dict[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4LSBpgo * val + else: + symbolic_array_full_dict[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4LSBpgo * val + + if ground_temp: + if par.T4LSBpgo is not None: + j = k = ell = 0 + for m in range(nvar[2]): + val = 0 + for jj in range(nvar[1]): + val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) + if m == 0: + symbolic_array_full_dict[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4LSBpgo * val + else: + symbolic_array_full_dict[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.T4LSBpgo * val + + if ocean: + + # deltaT_o part + for i in range(nvar[3]): + + # t_full = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='dok') + + j = k = ell = 0 + for m in range(nvar[1]): + val = 0 + for jj in range(nvar[3]): + val += U_inv[i, jj] * bips.Z(jj, j, k, ell, m) + if m == 0: + symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + else: + symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4sbpa * val + + for m in range(nvar[3]): + val = 0 + for jj in range(nvar[3]): + val -= U_inv[i, jj] * bips.V(jj, j, k, ell, m) + if m == 0: + symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4sbpgo * val + else: + symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4sbpgo * val + + # deltaT_g part + if ground_temp: + for i in range(nvar[2]): + + # t_full = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='dok') + + j = k = ell = 0 + for m in range(nvar[1]): + val = 0 + for jj in range(nvar[2]): + val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] + if m == 0: + symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = par.T4sbpa * val + else: + symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * par.T4sbpa * val + + for m in range(nvar[2]): + val = 0 + for jj in range(nvar[2]): + val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] + if m == 0: + symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = par.T4sbpgo * val + else: + symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * par.T4sbpgo * val + + return symbolic_array_full_dict + + def compute_tensor(self): + """Routine to compute the tensor.""" + # gathering + par = self.params + ndim = par.ndim + + sparse_arrays_dict = SymbolicTensorLinear._compute_tensor_dicts(self) + symbolic_array_full_dict = self._compute_tensor_dicts() + + tensor = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='coo') + if sparse_arrays_dict is not None: + tensor = self._add_dict_to_tensor(sparse_arrays_dict, tensor) + if symbolic_array_full_dict is not None: + tensor = self._add_dict_to_tensor(symbolic_array_full_dict, tensor) + self._set_tensor(tensor) + def _kronecker_delta(i, j): From 07518fa09d95250e2b2307f2553a655184035ec0 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 11 Jul 2023 15:07:44 +0200 Subject: [PATCH 008/143] Update to parameter locations --- qgs/tensors/symbolic_qgtensor.py | 86 ++++++++++++++++---------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index c0bcfcf..cc1f618 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -220,7 +220,7 @@ def _compute_tensor_dicts(self): aips = self.atmospheric_inner_products par = self.params - symbolic_params = self.params.symbolic_params + symbolic_params = self.sym_params atp = par.atemperature_params ap = par.atmospheric_params op = par.oceanic_params @@ -260,7 +260,7 @@ def _compute_tensor_dicts(self): a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[(i, j)] = ap.sig0 * aips.a(i, j) - aips.u(i, j) + a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = sy.matrices.Inverse(a_theta) @@ -277,7 +277,7 @@ def _compute_tensor_dicts(self): M_psio = dict() for i in range(offset, nvar[3]): for j in range(offset, nvar[3]): - M_psio[(i - offset, j - offset)] = bips.M(i, j) + par.G * bips.U(i, j) + M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.G * bips.U(i, j) M_psio = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) M_psio = sy.matrices.Inverse(M_psio) @@ -307,7 +307,7 @@ def _compute_tensor_dicts(self): jo = j + offset # skipping the theta 0 variable if it exists val = a_inv[i, :] * aips._c[offset:, jo] - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= val * scp.beta + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= val * symbolic_params['beta'] symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 @@ -317,11 +317,11 @@ def _compute_tensor_dicts(self): if gp.hk is not None: #//TODO: Need to make this symbolic if gp.orographic_basis == "atmospheric": - oro = a_inv[i, :] * aips._g[offset:, jo, offset:] * self.sym_params['hk'] # not perfect + oro = a_inv[i, :] * aips._g[offset:, jo, offset:] * symbolic_params['hk'] # not perfect else: #//TODO: Need to make this symbolic # TODO: Can only be used with symbolic inner products here - a warning or an error should be raised if this is not the case. - oro = a_inv[i, :] * aips._gh[offset:, jo, offset:] * self.sym_params['hk'] # not perfect + oro = a_inv[i, :] * aips._gh[offset:, jo, offset:] * symbolic_params['hk'] # not perfect symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= oro / 2 symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] += oro / 2 @@ -378,7 +378,7 @@ def _compute_tensor_dicts(self): for j in range(nvar[1]): val = a_theta[i, :] @ aips._u[:, j] if par.Lpa is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.sym_params['sc'] * self.Lpa + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * symbolic_params['sc'] * self.Lpa if par.LSBpa is not None: symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.LSBpa @@ -389,21 +389,21 @@ def _compute_tensor_dicts(self): for j in range(nvar[2]): jo = j + offset # skipping the theta 0 variable if it exists val = - a_theta[i, :] @ aips._d[:, jo] - symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * self.sym_params['kd'] / 2 + symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * symbolic_params['kd'] / 2 if par.Lpa is not None: for j in range(nvar[3]): val = - a_theta[i, :] @ aips._s[:, j] symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.Lpa / 2 - if par.LSBpgo is not None: + if self.LSBpgo is not None: symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.LSBpgo if ground_temp: - if par.Lpa is not None: + if self.Lpa is not None: for j in range(nvar[2]): val = - a_theta[i, :] @ aips._s[:, j] symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.Lpa / 2 - if par.LSBpgo is not None: + if self.LSBpgo is not None: symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.LSBpgo if ocean: @@ -411,17 +411,17 @@ def _compute_tensor_dicts(self): for i in range(nvar[2]): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - val = M_psio[i, :] @ bips._K[offset:, jo] * self.sym_params['d'] + val = M_psio[i, :] @ bips._K[offset:, jo] * symbolic_params['d'] symbolic_array_dic[(self._psi_o(i), self._psi_a(j), 0)] += val symbolic_array_dic[(self._psi_o(i), self._theta_a(jo), 0)] -= val for j in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists val = - M_psio[i, :] @ bips._N[offset:, jo] - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * self.sym_params['beta'] + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * symbolic_params['beta'] val = - M_psio[i, :] @ bips._M[offset:, jo] - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * (self.sym_params['r'] + self.sym_params['d']) + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * (symbolic_params['r'] + symbolic_params['d']) for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists @@ -434,13 +434,13 @@ def _compute_tensor_dicts(self): for j in range(nvar[1]): val = U_inv[i, :] @ bips._W[:, j] - symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * self.sym_params['sc'] * self.Lpgo - if par.sbpa is not None: + symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo + if self.sbpa is not None: symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * self.sbpa for j in range(nvar[3]): symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) - if par.sbpgo is not None: + if self.sbpgo is not None: symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) for j in range(nvar[2]): @@ -456,14 +456,14 @@ def _compute_tensor_dicts(self): for j in range(nvar[1]): val = U_inv[i, :] @ bips._W[:, j] - symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * self.sym_params['sc'] * self.Lpgo + symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo if self.sbpa is not None: symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * self.sbpa for j in range(nvar[2]): symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) if self.sbpgo is not None: - symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] += - par.sbpgo * _kronecker_delta(i, j) + symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) else: # psi_a part @@ -475,19 +475,19 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.c(offset + jj, jo) - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= val * self.sym_params['beta'] + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= val * symbolic_params['beta'] - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (ap.kd * _kronecker_delta(i, j)) / 2 + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] = (ap.kd * _kronecker_delta(i, j)) / 2 #//TODO: what is gp.hk parameter??? if gp is not None: - if gp.hk is not None: + if symbolic_params['hk'] is not None: oro = 0 if gp.orographic_basis == "atmospheric": for jj in range(nvar[0]): for kk in range(nvar[0]): - oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * gp.hk[kk] + oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * self.hk[kk] else: for jj in range(nvar[0]): for kk in range(nvar[0]): @@ -513,17 +513,17 @@ def _compute_tensor_dicts(self): # theta_a part for i in range(nvar[1]): - if par.Cpa is not None: + if self.Cpa is not None: for jj in range(nvar[1]): for kk in range(nvar[1]): symbolic_array_dic[(self._theta_a(i), 0, 0)] -= a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk] - if atp.hd is not None and self.thetas is not None: + if symbolic_params['hd'] is not None and self.thetas is not None: val = 0 for jj in range(nvar[1]): for kk in range(nvar[1]): val -= a_theta[i, jj] * aips.u(jj, kk) * self.thetas[kk] - symbolic_array_dic[(self._theta_a(i), 0, 0)] += val * atp.hd + symbolic_array_dic[(self._theta_a(i), 0, 0)] += val * symbolic_params['hd'] for j in range(nvar[0]): @@ -532,16 +532,16 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.a(jj, jo) - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += val * self.sym_params['kd'] * self.sig0 / 2 - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= val * (self.sym_params['kd'] / 2 + 2 * self.sym_params['kdp']) * self.sig0 + symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += val * symbolic_params['kd'] * self.sig0 / 2 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.c(jj, jo) - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] += val * self.sym_params['beta'] * self.sig0 + symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] += val * symbolic_params['beta'] * self.sig0 if gp is not None: - if gp.hk is not None: + if symbolic_params['hk'] is not None: oro = 0 if gp.orographic_basis == "atmospheric": for jj in range(nvar[1]): @@ -573,13 +573,13 @@ def _compute_tensor_dicts(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips.u(jj, j) - if par.Lpa is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.sym_params['sc'] * self.Lpa - if par.LSBpa is not None: + if self.Lpa is not None: + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * symbolic_params['sc'] * self.Lpa + if self.LSBpa is not None: symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.LSBpa - if atp.hd is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * atp.hd + if symbolic_params['hd'] is not None: + symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * symbolic_params['hd'] if ocean: for j in range(nvar[2]): @@ -587,15 +587,15 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.d(jj, jo) - symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * self.sym_params['kd'] / 2 + symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * symbolic_params['kd'] / 2 - if par.Lpa is not None: + if self.Lpa is not None: for j in range(nvar[3]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.Lpa / 2 - if par.LSBpgo is not None: + if self.LSBpgo is not None: symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.LSBpgo if ground_temp: @@ -615,7 +615,7 @@ def _compute_tensor_dicts(self): jo = j + offset # skipping the theta 0 variable if it exists for jj in range(nvar[2]): - val = M_psio[i, jj] * bips.K(offset + jj, jo) * self.sym_params['d'] + val = M_psio[i, jj] * bips.K(offset + jj, jo) * symbolic_params['d'] symbolic_array_dic[(self._psi_o(i), self._psi_a(j), 0)] += val symbolic_array_dic[(self._psi_o(i), self._theta_a(jo), 0)] -= val @@ -625,12 +625,12 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.N(offset + jj, jo) - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * self.sym_params['beta'] + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * symbolic_params['beta'] val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.M(offset + jj, jo) - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * (self.sym_params['r'] + self.sym_params['d']) + symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * (symbolic_params['r'] + symbolic_params['d']) for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists @@ -647,7 +647,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips.W(jj, j) - symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * self.sym_params['sc'] * self.Lpgo + symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo if par.sbpa is not None: symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * self.sbpa @@ -673,7 +673,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips.W(jj, j) - symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * self.sym_params['sc'] * self.Lpgo + symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo if par.sbpa is not None: symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * self.sbpa From e02720f594b118195063553d697d405abbd4c206 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 11 Jul 2023 15:40:59 +0200 Subject: [PATCH 009/143] edit to subs in IP --- qgs/inner_products/symbolic.py | 27 ++++++++++++++++++++------- qgs/tensors/symbolic_qgtensor.py | 30 +++++++++++++++--------------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 8f1a6ae..ce165bc 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -324,8 +324,10 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - - subs = self.subs + self.atmospheric_basis.substitutions + self.ground_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions + self.ground_basis.substitutions ngr = len(ground_basis) if orographic_basis == "atmospheric": @@ -804,7 +806,10 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None with Pool(max_workers=num_threads) as pool: #//TODO: Check if this needs to be edited for symbolic ip - subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions natm = len(atmosphere_basis) self._K = sp.zeros((self.noc, natm), dtype=float, format='dok') @@ -881,8 +886,10 @@ def compute_inner_products(self, num_threads=None, timeout=None): num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - - subs = self.subs + self.oceanic_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.oceanic_basis.substitutions # N inner products args_list = [[(i, j), self.ip.ip_diff_x, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] @@ -1227,7 +1234,10 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None with Pool(max_workers=num_threads) as pool: #//TODO: Check if this needs changing for symbolic IP. - subs = self.subs + self.ground_basis.substitutions + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.ground_basis.substitutions + self.atmospheric_basis.substitutions natm = len(atmosphere_basis) self._W = sp.zeros((self.ngr, natm), dtype=float, format='dok') @@ -1286,7 +1296,10 @@ def compute_inner_products(self, num_threads=None, timeout=None): num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - subs = self.subs + self.ground_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.ground_basis.substitutions # U inner products args_list = [[(i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j))] for i in range(self.ngr) for j in range(self.ngr)] diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index cc1f618..900032a 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -338,7 +338,7 @@ def _compute_tensor_dicts(self): # theta_a part for i in range(nvar[1]): - if par.Cpa is not None: + if self.Cpa is not None: symbolic_array_dic[(self._theta_a(i), 0, 0)] -= a_theta[i, :] * aips._u * self.Cpa # not perfect if atp.hd is not None and atp.thetas is not None: @@ -377,9 +377,9 @@ def _compute_tensor_dicts(self): for j in range(nvar[1]): val = a_theta[i, :] @ aips._u[:, j] - if par.Lpa is not None: + if self.Lpa is not None: symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * symbolic_params['sc'] * self.Lpa - if par.LSBpa is not None: + if self.LSBpa is not None: symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.LSBpa if atp.hd is not None: @@ -391,7 +391,7 @@ def _compute_tensor_dicts(self): val = - a_theta[i, :] @ aips._d[:, jo] symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * symbolic_params['kd'] / 2 - if par.Lpa is not None: + if self.Lpa is not None: for j in range(nvar[3]): val = - a_theta[i, :] @ aips._s[:, j] symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.Lpa / 2 @@ -648,12 +648,12 @@ def _compute_tensor_dicts(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips.W(jj, j) symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo - if par.sbpa is not None: + if self.sbpa is not None: symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * self.sbpa for j in range(nvar[3]): symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) - if par.sbpgo is not None: + if self.sbpgo is not None: symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) for j in range(nvar[2]): @@ -674,12 +674,12 @@ def _compute_tensor_dicts(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips.W(jj, j) symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo - if par.sbpa is not None: + if self.sbpa is not None: symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * self.sbpa for j in range(nvar[2]): symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) - if par.sbpgo is not None: + if self.sbpgo is not None: symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) return symbolic_array_dic @@ -926,7 +926,7 @@ def _compute_non_stored_full_dict(self): for i in range(nvar[1]): # t_full = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='dok') - if par.T4LSBpa is not None: + if self.T4LSBpa is not None: j = k = ell = 0 for m in range(nvar[1]): val = 0 @@ -938,7 +938,7 @@ def _compute_non_stored_full_dict(self): symbolic_array_full_dict[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4LSBpa * val if ocean: - if par.T4LSBpgo is not None: + if self.T4LSBpgo is not None: j = k = ell = 0 for m in range(nvar[3]): val = 0 @@ -950,7 +950,7 @@ def _compute_non_stored_full_dict(self): symbolic_array_full_dict[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4LSBpgo * val if ground_temp: - if par.T4LSBpgo is not None: + if self.T4LSBpgo is not None: j = k = ell = 0 for m in range(nvar[2]): val = 0 @@ -999,18 +999,18 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = par.T4sbpa * val + symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val else: - symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * par.T4sbpa * val + symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4sbpa * val for m in range(nvar[2]): val = 0 for jj in range(nvar[2]): val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = par.T4sbpgo * val + symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4sbpgo * val else: - symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * par.T4sbpgo * val + symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.T4sbpgo * val return symbolic_array_full_dict From 05738948a175f5450bb7c00d06de622ee7283e1e Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 11 Jul 2023 16:38:30 +0200 Subject: [PATCH 010/143] Bug fixing the symbolic tensor --- notebooks/Symbolic Output.ipynb | 257 +++++++++++++++++++++++++++++-- qgs/tensors/symbolic_qgtensor.py | 5 +- 2 files changed, 248 insertions(+), 14 deletions(-) diff --git a/notebooks/Symbolic Output.ipynb b/notebooks/Symbolic Output.ipynb index 95d3b18..d052069 100644 --- a/notebooks/Symbolic Output.ipynb +++ b/notebooks/Symbolic Output.ipynb @@ -21,6 +21,16 @@ "from qgs.params.params import QgParams" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf632fac", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters = QgParams(dynamic_T=True)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -28,7 +38,6 @@ "metadata": {}, "outputs": [], "source": [ - "model_parameters = QgParams(dynamic_T=True)\n", "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", "# Mode truncation at the wavenumber 2 in the x and at the \n", "# wavenumber 4 in the y spatial coordinates for the ocean\n", @@ -62,10 +71,9 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.inner_products.analytic import AtmosphericAnalyticInnerProducts, OceanicAnalyticInnerProducts, GroundAnalyticInnerProducts\n", - "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, GroundSymbolicInnerProducts\n", + "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicLinTensor" + "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear" ] }, { @@ -77,28 +85,122 @@ }, "outputs": [], "source": [ - "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters)\n", - "ocn_ip = OceanicSymbolicInnerProducts(model_parameters)" + "%%time\n", + "# atm_ip = AtmosphericSymbolicInnerProducts(model_parameters)\n", + "# ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", + "ocn_ip = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6dcb5b8c", + "metadata": {}, + "outputs": [], + "source": [ + "ocn_ip.return_symbolic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4c7717f", + "metadata": {}, + "outputs": [], + "source": [ + "import sympy as sy" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c5064c91", + "metadata": {}, + "outputs": [], + "source": [ + "# U_inv = dict()\n", + "\n", + "# for i in range(model_parameters.number_of_variables[3]):\n", + "# for j in range(model_parameters.number_of_variables[3]):\n", + "# U_inv[(i, j)] = ocn_ip.U(i, j)\n", + "# U_inv = sy.matrices.immutable.ImmutableSparseMatrix(model_parameters.number_of_variables[3], model_parameters.number_of_variables[3], U_inv)\n", + "# U_inv = sy.matrices.Inverse(U_inv)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e28d4eee", + "metadata": {}, + "outputs": [], + "source": [ + "# U_inv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2d3c783", + "metadata": {}, + "outputs": [], + "source": [ + "# U_inv = dict()\n", + "\n", + "# for i in range(model_parameters.number_of_variables[3]):\n", + "# for j in range(model_parameters.number_of_variables[3]):\n", + "# U_inv[(i, j)] = ocn_ip_stored.U(i, j)\n", + "# U_inv = sy.matrices.immutable.ImmutableSparseMatrix(model_parameters.number_of_variables[3], model_parameters.number_of_variables[3], U_inv)\n", + "# U_inv = sy.matrices.Inverse(U_inv)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2512cef4", + "metadata": {}, + "outputs": [], + "source": [ + "# U_inv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "041f2236", + "metadata": {}, + "outputs": [], + "source": [ + "# ocn_ip_stored._Z.shape" ] }, { "cell_type": "code", "execution_count": null, - "id": "81d1676b", + "id": "768c0226", "metadata": {}, "outputs": [], "source": [ - "sym_ten = SymbolicLinTensor(model_parameters, atm_ip, ocn_ip)" + "# from sympy import tensorproduct" ] }, { "cell_type": "code", "execution_count": null, - "id": "8037bbb9", + "id": "e006ef35", "metadata": {}, "outputs": [], "source": [ - "sym_ten.LR" + "# x = U_inv[1, :] * ocn_ip_stored._Z[:, :, :, :, :]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78bb6c3a", + "metadata": {}, + "outputs": [], + "source": [ + "# x.shape" ] }, { @@ -108,13 +210,144 @@ "metadata": {}, "outputs": [], "source": [ - "model_parameters.symbolic_params['atm_T0']" + "# model_parameters.symbolic_params['atm_T0']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35d77653", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", + "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70086b90", + "metadata": {}, + "outputs": [], + "source": [ + "# atm_ip_stored.save_to_file('temp_atm_sym.ip')\n", + "# ocn_ip_stored.save_to_file('temp_ocn_sym.ip')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea85360f", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters)\n", + "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "196d5440", + "metadata": {}, + "outputs": [], + "source": [ + "atm_loaded.load_from_file('temp_atm_sym.ip')\n", + "ocn_loaded.load_from_file('temp_ocn_sym.ip')" + ] + }, + { + "cell_type": "markdown", + "id": "d43d341b", + "metadata": {}, + "source": [ + "## Testing the symbolic_qgtensor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e17b891", + "metadata": {}, + "outputs": [], + "source": [ + "ten = SymbolicTensorLinear(params=model_parameters, atmospheric_inner_products=atm_loaded, oceanic_inner_products=ocn_loaded)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf668cb9", + "metadata": {}, + "outputs": [], + "source": [ + "ten.compute_tensor()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91d6fde6", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7d9a482", + "metadata": {}, + "outputs": [], + "source": [ + "a_inv = dict()\n", + "for i in range(0, 10):\n", + " for j in range(0, 10):\n", + " a_inv[(i - 0, j - 0)] = atm_loaded.a(i, j)\n", + "\n", + "a_inv = sy.matrices.immutable.ImmutableSparseMatrix(10, 10, a_inv)\n", + "a_inv = sy.matrices.Inverse(a_inv)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "06362de1", + "metadata": {}, + "outputs": [], + "source": [ + "a_inv" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "88a32f70", + "metadata": {}, + "outputs": [], + "source": [ + "atm_loaded._c[1:, 2].shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6c23fca", + "metadata": {}, + "outputs": [], + "source": [ + "a_inv[1, :] * atm_loaded._c[1:, 2]" ] }, { "cell_type": "code", "execution_count": null, - "id": "f3e895e2", + "id": "c23f807c", "metadata": {}, "outputs": [], "source": [] diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 900032a..5c74c26 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -305,9 +305,10 @@ def _compute_tensor_dicts(self): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - + #//TODO: A =- was converted to = here, I need to make sure this doesnt alter the results val = a_inv[i, :] * aips._c[offset:, jo] - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= val * symbolic_params['beta'] + print(val) + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] = val * symbolic_params['beta'] symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 From d85c1da53b90bfca0e5b63d299ba617a9071ba85 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 14 Jul 2023 16:15:54 +0200 Subject: [PATCH 011/143] working out a way to take tensor products --- qgs/inner_products/symbolic.py | 8 +++---- qgs/params/params.py | 37 +++++++++++++++++++++++++------ qgs/tensors/symbolic_qgtensor.py | 38 +++++++++++++++++++------------- 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index ce165bc..08f4016 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -481,8 +481,8 @@ def compute_inner_products(self, num_threads=None, timeout=None): if self._T4 or self._dynamic_T: if self.return_symbolic: self._z = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm, self.natm, self.natm)) - else: - self._z = self._z.to_coo() + else: + self._z = self._z.to_coo() @property def natm(self): @@ -954,8 +954,8 @@ def compute_inner_products(self, num_threads=None, timeout=None): if self._T4 or self._dynamic_T: if self.return_symbolic: self._V = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc, self.noc, self.noc)) - else: - self._V = self._V.to_coo() + else: + self._V = self._V.to_coo() @property def noc(self): diff --git a/qgs/params/params.py b/qgs/params/params.py index 35d2eaf..4aa5f4c 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -874,7 +874,8 @@ class QgParams(Params): 'hlambda': sy.Symbol('lambda'), # Ground Parameters - 'hk': sy.Symbol('h_k'), + 'hk_val': sy.Symbol('h_k'), + 'hk': None, # Ground Temperature Parameters 'gnd_gamma': sy.Symbol('gamma_g'), @@ -1251,12 +1252,17 @@ def print_params(self): print(s) def symbolic_insolation_array(self, Cpa=None, Cpgo=None): - """Set the array Cpa and Cpgo from given data, or the default symbols + """Set the array Cpa and Cpgo from given value, or the default symbols + It is assumed that the values given are either for Cpa, or Cpgo, there is no option to set both Cpa and Cpgo at the same time. + Parameters ---------- - values: List(sympy Symbol) - A list of the sympy symbols that will be converted to an ImmutableSparseNDimArray. + Cpa: List(sympy Symbol, float) + A list of the sympy symbols, or floats, that will be converted to an ImmutableSparseNDimArray. + + Cpgo: List(sympy Symbol, float) + A list of the sympy symbols, or floats, that will be converted to an ImmutableSparseNDimArray. """ if Cpa is not None: @@ -1279,9 +1285,26 @@ def symbolic_insolation_array(self, Cpa=None, Cpgo=None): gnd_C_list[1] = self.symbolic_params['gnd_C_val'] ocn_C_list[1] = self.symbolic_params['ocn_C_val'] - self.symbolic_params['atm_C'] = sy.tensor.array.ImmutableSparseNDimArray(atm_C_list) - self.symbolic_params['gnd_C'] = sy.tensor.array.ImmutableSparseNDimArray(gnd_C_list) - self.symbolic_params['ocn_C'] = sy.tensor.array.ImmutableSparseNDimArray(ocn_C_list) + self.symbolic_params['atm_C'] = sy.matrices.immutable.ImmutableSparseMatrix(atm_C_list) + self.symbolic_params['gnd_C'] = sy.matrices.immutable.ImmutableSparseMatrix(gnd_C_list) + self.symbolic_params['ocn_C'] = sy.matrices.immutable.ImmutableSparseMatrix(ocn_C_list) + + def symbolic_orography_array(self, hk=None): + """Set the array hk from given value, or the defulat symbols + + Parameters + ---------- + hk: List(sympy Symbol, float) + A list of the sympy symbols, or floats, that will be converted to an ImmutableSparseNDimArray. + + """ + if hk is not None: + oro_list = hk + else: + oro_list = [0] * self.nmod[0] + oro_list[1] = self.symbolic_params['hk_val'] + + self.symbolic_params['hk'] = sy.matrices.immutable.ImmutableSparseMatrix(oro_list) @property def ndim(self): diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 5c74c26..0746086 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -256,6 +256,7 @@ def _compute_tensor_dicts(self): a_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[0], nvar[0], a_inv) a_inv = sy.matrices.Inverse(a_inv) + a_inv = a_inv.simplify() a_theta = dict() for i in range(nvar[1]): @@ -264,6 +265,7 @@ def _compute_tensor_dicts(self): a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = sy.matrices.Inverse(a_theta) + a_theta = a_theta.simplify() if bips is not None: if ocean: @@ -273,6 +275,7 @@ def _compute_tensor_dicts(self): U_inv[(i, j)] = bips.U(i, j) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) U_inv = sy.matrices.Inverse(U_inv) + U_inv = U_inv.simplify() M_psio = dict() for i in range(offset, nvar[3]): @@ -281,6 +284,7 @@ def _compute_tensor_dicts(self): M_psio = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) M_psio = sy.matrices.Inverse(M_psio) + M_psio = M_psio.simplify() else: U_inv = dict() @@ -289,6 +293,7 @@ def _compute_tensor_dicts(self): U_inv[(i, j)] = bips.U(i, j) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(U_inv) + U_inv = U_inv.simplify() ################ @@ -301,14 +306,14 @@ def _compute_tensor_dicts(self): if aips.stored and go: # psi_a part + a_inv_mult_c = a_inv @ aips._c[offset:, offset:] + a_inv_mult_g = a_inv @ aips._g[offset:, :, offset:] for i in range(nvar[0]): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists #//TODO: A =- was converted to = here, I need to make sure this doesnt alter the results - val = a_inv[i, :] * aips._c[offset:, jo] - print(val) - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] = val * symbolic_params['beta'] + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] = a_inv_mult_c[i, j] * symbolic_params['beta'] symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 @@ -316,26 +321,29 @@ def _compute_tensor_dicts(self): if gp is not None: # convert if gp.hk is not None: - #//TODO: Need to make this symbolic + #//TODO: Can this be more efficient, make these slice matricies outside of the i loop? if gp.orographic_basis == "atmospheric": - oro = a_inv[i, :] * aips._g[offset:, jo, offset:] * symbolic_params['hk'] # not perfect + _g_slice = sy.matrices.immutable.ImmutableSparseMatrix(aips._g[offset:, jo, offset:]) + oro = a_inv @ _g_slice @ symbolic_params['hk'] # not perfect else: - #//TODO: Need to make this symbolic + #//TODO: Can this be more efficient, make these slice matricies outside of the i loop? + # TODO: Can only be used with symbolic inner products here - a warning or an error should be raised if this is not the case. - oro = a_inv[i, :] * aips._gh[offset:, jo, offset:] * symbolic_params['hk'] # not perfect - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= oro / 2 - symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] += oro / 2 + _gh_slice = sy.matrices.immutable.ImmutableSparseMatrix(aips._gh[offset:, jo, offset:]) + oro = a_inv @ _gh_slice @ symbolic_params['hk'] # not perfect + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] = oro[i] / 2 + symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] += oro[i] / 2 + _b_slice = sy.matrices.immutable.ImmutableSparseMatrix(aips._b[offset:, jo, :offset]) + val = a_inv @ _b_slice for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists - val = a_inv[i, :] * aips._b[offset:, jo, ko] - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), self._psi_a(k))] = - val - symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), self._theta_a(ko))] = - val + symbolic_array_dic[(self._psi_a(i), self._psi_a(j), self._psi_a(k))] = - val[i, k] + symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), self._theta_a(ko))] = - val[i, k] if ocean: + a_inv_mult_d = a_inv @ aips._d[offset:, offset:] for j in range(nvar[2]): - jo = j + offset # skipping the theta 0 variable if it exists - val = a_inv[i, :] * aips._d[offset:, jo] - symbolic_array_dic[(self._psi_a(i), self._psi_o(j), 0)] += val * symbolic_params['kd'] / 2 + symbolic_array_dic[(self._psi_a(i), self._psi_o(j), 0)] += a_inv_mult_d[i, j] * symbolic_params['kd'] / 2 # theta_a part for i in range(nvar[1]): From 296397675465f721061ece6326c1483edc6c7520 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 18 Jul 2023 15:14:21 +0200 Subject: [PATCH 012/143] Addition of tensordot and calculating inverse matricies properly --- qgs/tensors/symbolic_qgtensor.py | 53 ++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 0746086..26b6337 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -255,7 +255,7 @@ def _compute_tensor_dicts(self): a_inv[(i - offset, j - offset)] = aips.a(i, j) a_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[0], nvar[0], a_inv) - a_inv = sy.matrices.Inverse(a_inv) + a_inv = a_inv.inverse() a_inv = a_inv.simplify() a_theta = dict() @@ -264,7 +264,7 @@ def _compute_tensor_dicts(self): a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) - a_theta = sy.matrices.Inverse(a_theta) + a_theta = a_theta.inverse() a_theta = a_theta.simplify() if bips is not None: @@ -274,7 +274,7 @@ def _compute_tensor_dicts(self): for j in range(nvar[3]): U_inv[(i, j)] = bips.U(i, j) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) - U_inv = sy.matrices.Inverse(U_inv) + U_inv = U_inv.inverse() U_inv = U_inv.simplify() M_psio = dict() @@ -283,7 +283,7 @@ def _compute_tensor_dicts(self): M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.G * bips.U(i, j) M_psio = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) - M_psio = sy.matrices.Inverse(M_psio) + M_psio = M_psio.inverse() M_psio = M_psio.simplify() else: @@ -292,7 +292,7 @@ def _compute_tensor_dicts(self): for j in range(nvar[2]): U_inv[(i, j)] = bips.U(i, j) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(U_inv) + U_inv = U_inv.inverse() U_inv = U_inv.simplify() ################ @@ -308,6 +308,7 @@ def _compute_tensor_dicts(self): # psi_a part a_inv_mult_c = a_inv @ aips._c[offset:, offset:] a_inv_mult_g = a_inv @ aips._g[offset:, :, offset:] + for i in range(nvar[0]): for j in range(nvar[0]): @@ -813,7 +814,7 @@ def _compute_stored_full_dict(self): for j in range(nvar[1]): a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) - a_theta = sy.matrices.Inverse(a_theta) + a_theta = a_theta.inverse() if bips is not None: U_inv = dict() @@ -822,7 +823,7 @@ def _compute_stored_full_dict(self): for j in range(nvar[3]): U_inv[(i, j)] = bips.U(i, j) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) - U_inv = sy.matrices.Inverse(U_inv) + U_inv = U_inv.inverse() else: for i in range(nvar[2]): for j in range(nvar[2]): @@ -925,7 +926,7 @@ def _compute_non_stored_full_dict(self): U_inv[(i, j)] = bips.U(i, j) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) - U_inv = sy.matrices.Inverse(U_inv) + U_inv = U_inv.inverse() ################# @@ -1046,4 +1047,38 @@ def _kronecker_delta(i, j): return 1 else: - return 0 \ No newline at end of file + return 0 + +def _symbolic_tensordot(a, b, axes=2): + """ + Compute tensor dot product along specified axes of two sympy symbolic arrays + + This is based on numpy.tensordot + + Parameters + ---------- + a, b: sympy arrays + Tensors to "dot" + + axes: int + * integer_like + If an int N, sum over the last N axes of `a` and the first N axes + of `b` in order. The sizes of the corresponding axes must match. + + Returns + ------- + output: sympy tensor + The tensor dot product of the input + + """ + as_ = a.shape + nda = len(as_) + + a_com = [nda+i for i in range(-axes, 0)] + b_com = [nda+i for i in range(axes)] + sum_cols = tuple(a_com + b_com) + + prod = sy.tensorproduct(a, b) + + return sy.tensorcontraction(prod, sum_cols) + \ No newline at end of file From 411cb28111552612033371ec59df735c86e2cbc3 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 18 Jul 2023 18:36:53 +0200 Subject: [PATCH 013/143] Update of addition to dict --- qgs/tensors/symbolic_qgtensor.py | 192 ++++++++++++++++--------------- 1 file changed, 102 insertions(+), 90 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 26b6337..10b22d7 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -302,7 +302,7 @@ def _compute_tensor_dicts(self): else: go = True - symbolic_array_dic = dict() + sy_arr_dic = dict() if aips.stored and go: # psi_a part @@ -314,10 +314,9 @@ def _compute_tensor_dicts(self): jo = j + offset # skipping the theta 0 variable if it exists #//TODO: A =- was converted to = here, I need to make sure this doesnt alter the results - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] = a_inv_mult_c[i, j] * symbolic_params['beta'] - - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 - symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] = (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), a_inv_mult_c[i, j] * symbolic_params['beta']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -(symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) if gp is not None: # convert @@ -332,39 +331,42 @@ def _compute_tensor_dicts(self): # TODO: Can only be used with symbolic inner products here - a warning or an error should be raised if this is not the case. _gh_slice = sy.matrices.immutable.ImmutableSparseMatrix(aips._gh[offset:, jo, offset:]) oro = a_inv @ _gh_slice @ symbolic_params['hk'] # not perfect - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] = oro[i] / 2 - symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] += oro[i] / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), oro[i] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i] / 2) _b_slice = sy.matrices.immutable.ImmutableSparseMatrix(aips._b[offset:, jo, :offset]) val = a_inv @ _b_slice for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), self._psi_a(k))] = - val[i, k] - symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), self._theta_a(ko))] = - val[i, k] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), self._psi_a(k)), -val[i, k]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), self._theta_a(ko)), -val[i, k]) + if ocean: a_inv_mult_d = a_inv @ aips._d[offset:, offset:] for j in range(nvar[2]): - symbolic_array_dic[(self._psi_a(i), self._psi_o(j), 0)] += a_inv_mult_d[i, j] * symbolic_params['kd'] / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * symbolic_params['kd'] / 2) # theta_a part for i in range(nvar[1]): if self.Cpa is not None: - symbolic_array_dic[(self._theta_a(i), 0, 0)] -= a_theta[i, :] * aips._u * self.Cpa # not perfect + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -a_theta[i, :] * aips._u * self.Cpa) if atp.hd is not None and atp.thetas is not None: val = - a_theta[i, :] * aips._u * sp.COO(atp.thetas.astype(float)) # not perfect - symbolic_array_dic[(self._theta_a(i), 0, 0)] += val * symbolic_params['hd'] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val * symbolic_params['hd']) for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists val = a_theta[i, :] @ aips._a[:, jo] - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += val * symbolic_params['kd'] * self.sig0 / 2 - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 + val_2 = val * symbolic_params['kd'] * self.sig0 / 2 + val_3 = val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val_2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -val_3) val = - a_theta[i, :] @ aips._c[:, jo] - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] += val * symbolic_params['beta'] * self.sig0 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), val * symbolic_params['beta'] * self.sig0) if gp is not None: if gp.hk is not None: @@ -373,48 +375,48 @@ def _compute_tensor_dicts(self): oro = a_theta[i, :] @ aips._g[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect else: oro = a_theta[i, :] @ aips._gh[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= self.sig0 * oro / 2 - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += self.sig0 * oro / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -self.sig0 * oro / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = a_theta[i, :] @ aips._b[:, jo, ko] - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), self._theta_a(ko))] = - val * self.sig0 - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), self._psi_a(k))] = - val * self.sig0 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - val * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - val * self.sig0) val = a_theta[i, :] @ aips._g[:, jo, ko] - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), self._theta_a(ko))] += val + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), val) for j in range(nvar[1]): val = a_theta[i, :] @ aips._u[:, j] if self.Lpa is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * symbolic_params['sc'] * self.Lpa + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * symbolic_params['sc'] * self.Lpa) if self.LSBpa is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.LSBpa + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.LSBpa) if atp.hd is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * atp.hd + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.hd) if ocean: for j in range(nvar[2]): jo = j + offset # skipping the theta 0 variable if it exists val = - a_theta[i, :] @ aips._d[:, jo] - symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * symbolic_params['kd'] / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * self.sig0 * symbolic_params['kd'] / 2) if self.Lpa is not None: for j in range(nvar[3]): val = - a_theta[i, :] @ aips._s[:, j] - symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.Lpa / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.Lpa / 2) if self.LSBpgo is not None: - symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.LSBpgo + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.LSBpgo) if ground_temp: if self.Lpa is not None: for j in range(nvar[2]): val = - a_theta[i, :] @ aips._s[:, j] - symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.Lpa / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.Lpa / 2) if self.LSBpgo is not None: - symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.LSBpgo + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.LSBpgo) if ocean: # psi_o part @@ -422,58 +424,57 @@ def _compute_tensor_dicts(self): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists val = M_psio[i, :] @ bips._K[offset:, jo] * symbolic_params['d'] - symbolic_array_dic[(self._psi_o(i), self._psi_a(j), 0)] += val - symbolic_array_dic[(self._psi_o(i), self._theta_a(jo), 0)] -= val + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), val) for j in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists val = - M_psio[i, :] @ bips._N[offset:, jo] - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * symbolic_params['beta'] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * symbolic_params['beta']) val = - M_psio[i, :] @ bips._M[offset:, jo] - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * (symbolic_params['r'] + symbolic_params['d']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * (symbolic_params['r'] + symbolic_params['d'])) for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), self._psi_o(k))] -= M_psio[i, :] @ bips._C[offset:, jo, ko] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio[i, :] @ bips._C[offset:, jo, ko]) # deltaT_o part for i in range(nvar[3]): - - symbolic_array_dic[(self._deltaT_o(i), 0, 0)] += U_inv[i, :] @ bips._W @ sp.COO(self.Cpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv[i, :] @ bips._W @ sp.COO(self.Cpgo)) for j in range(nvar[1]): val = U_inv[i, :] @ bips._W[:, j] - symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * symbolic_params['sc'] * self.Lpgo) if self.sbpa is not None: - symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * self.sbpa + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * self.sbpa) for j in range(nvar[3]): - symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) if self.sbpgo is not None: - symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.sbpgo * _kronecker_delta(i, j)) for j in range(nvar[2]): for k in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists ko = k + offset # skipping the T 0 variable if it exists - symbolic_array_dic[(self._deltaT_o(i), self._psi_o(j), self._deltaT_o(ko))] -= U_inv[i, :] @ bips._O[:, jo, ko] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(ko)), -U_inv[i, :] @ bips._O[:, jo, ko]) # deltaT_g part if ground_temp: for i in range(nvar[2]): - symbolic_array_dic[(self._deltaT_g(i), 0, 0)] += U_inv[i, :] @ bips._W @ sp.COO(self.Cpgo) # not perfect + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv[i, :] @ bips._W @ sp.COO(self.Cpgo)) # not perfect for j in range(nvar[1]): val = U_inv[i, :] @ bips._W[:, j] - symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * symbolic_params['sc'] * self.Lpgo) if self.sbpa is not None: - symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * self.sbpa + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.sbpa) for j in range(nvar[2]): - symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) if self.sbpgo is not None: - symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.sbpgo * _kronecker_delta(i, j)) else: # psi_a part @@ -485,10 +486,9 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.c(offset + jj, jo) - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= val * symbolic_params['beta'] - - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2 - symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] = (ap.kd * _kronecker_delta(i, j)) / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - val * symbolic_params['beta']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd * _kronecker_delta(i, j)) / 2) #//TODO: what is gp.hk parameter??? if gp is not None: @@ -502,23 +502,23 @@ def _compute_tensor_dicts(self): for jj in range(nvar[0]): for kk in range(nvar[0]): oro += a_inv[i, jj] * aips.gh(offset + jj, j, offset + kk) * gp.hk[kk] - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), 0)] -= oro / 2 - symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), 0)] += oro / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - oro / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.b(offset + jj, jo, ko) - symbolic_array_dic[(self._psi_a(i), self._psi_a(j), self._psi_a(k))] = - val - symbolic_array_dic[(self._psi_a(i), self._theta_a(jo), self._theta_a(ko))] = - val + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), self._psi_a(k)), - val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), self._theta_a(ko)), - val) if ocean: for j in range(nvar[2]): jo = j + offset val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.d(offset + jj, jo) - symbolic_array_dic[(self._psi_a(i), self._psi_o(j), 0)] += val * ap.kd / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), val * ap.kd / 2) # theta_a part @@ -526,14 +526,14 @@ def _compute_tensor_dicts(self): if self.Cpa is not None: for jj in range(nvar[1]): for kk in range(nvar[1]): - symbolic_array_dic[(self._theta_a(i), 0, 0)] -= a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), - a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk]) if symbolic_params['hd'] is not None and self.thetas is not None: val = 0 for jj in range(nvar[1]): for kk in range(nvar[1]): val -= a_theta[i, jj] * aips.u(jj, kk) * self.thetas[kk] - symbolic_array_dic[(self._theta_a(i), 0, 0)] += val * symbolic_params['hd'] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val * symbolic_params['hd']) for j in range(nvar[0]): @@ -542,13 +542,13 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.a(jj, jo) - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += val * symbolic_params['kd'] * self.sig0 / 2 - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val * symbolic_params['kd'] * self.sig0 / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - val * (symbolic_params['kd'] / 2 - 2 * symbolic_params['kdp']) * self.sig0) val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.c(jj, jo) - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] += val * symbolic_params['beta'] * self.sig0 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), val * symbolic_params['beta'] * self.sig0) if gp is not None: if symbolic_params['hk'] is not None: @@ -561,22 +561,22 @@ def _compute_tensor_dicts(self): for jj in range(nvar[1]): for kk in range(nvar[0]): oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * self.hk[kk] - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), 0)] -= self.sig0 * oro / 2 - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), 0)] += self.sig0 * oro / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - self.sig0 * oro / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.b(jj, jo, ko) - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), self._theta_a(ko))] = - val * self.sig0 - symbolic_array_dic[(self._theta_a(i), self._theta_a(jo), self._psi_a(k))] = - val * self.sig0 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - val * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - val * self.sig0) val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.g(jj, jo, ko) - symbolic_array_dic[(self._theta_a(i), self._psi_a(j), self._theta_a(ko))] += val + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), val) for j in range(nvar[1]): val = 0 @@ -584,12 +584,12 @@ def _compute_tensor_dicts(self): val += a_theta[i, jj] * aips.u(jj, j) if self.Lpa is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * symbolic_params['sc'] * self.Lpa + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * symbolic_params['sc'] * self.Lpa) if self.LSBpa is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * self.LSBpa + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.LSBpa) if symbolic_params['hd'] is not None: - symbolic_array_dic[(self._theta_a(i), self._theta_a(j), 0)] += val * symbolic_params['hd'] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * symbolic_params['hd']) if ocean: for j in range(nvar[2]): @@ -597,16 +597,16 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.d(jj, jo) - symbolic_array_dic[(self._theta_a(i), self._psi_o(j), 0)] += val * self.sig0 * symbolic_params['kd'] / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * self.sig0 * symbolic_params['kd'] / 2) if self.Lpa is not None: for j in range(nvar[3]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) - symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.Lpa / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.Lpa / 2) if self.LSBpgo is not None: - symbolic_array_dic[(self._theta_a(i), self._deltaT_o(j), 0)] += val * self.LSBpgo + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.LSBpgo) if ground_temp: if self.Lpa is not None: @@ -614,9 +614,9 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) - symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.Lpa / 2 + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.Lpa / 2) if self.LSBpgo is not None: - symbolic_array_dic[(self._theta_a(i), self._deltaT_g(j), 0)] += val * self.LSBpgo + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.LSBpgo) if ocean: # psi_o part @@ -626,8 +626,8 @@ def _compute_tensor_dicts(self): for jj in range(nvar[2]): val = M_psio[i, jj] * bips.K(offset + jj, jo) * symbolic_params['d'] - symbolic_array_dic[(self._psi_o(i), self._psi_a(j), 0)] += val - symbolic_array_dic[(self._psi_o(i), self._theta_a(jo), 0)] -= val + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), - val) for j in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists @@ -635,64 +635,64 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.N(offset + jj, jo) - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * symbolic_params['beta'] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * symbolic_params['beta']) val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.M(offset + jj, jo) - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), 0)] += val * (symbolic_params['r'] + symbolic_params['d']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * (symbolic_params['r'] + symbolic_params['d'])) for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists for jj in range(nvar[2]): - symbolic_array_dic[(self._psi_o(i), self._psi_o(j), self._psi_o(k))] -= M_psio[i, jj] * bips.C(offset + jj, jo, ko) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio[i, jj] * bips.C(offset + jj, jo, ko)) # deltaT_o part for i in range(nvar[3]): for jj in range(nvar[1]): for kk in range(nvar[3]): - symbolic_array_dic[(self._deltaT_o(i), 0, 0)] += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj]) for j in range(nvar[1]): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips.W(jj, j) - symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * symbolic_params['sc'] * self.Lpgo) if self.sbpa is not None: - symbolic_array_dic[(self._deltaT_o(i), self._theta_a(j), 0)] += val * self.sbpa + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * self.sbpa) for j in range(nvar[3]): - symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) if self.sbpgo is not None: - symbolic_array_dic[(self._deltaT_o(i), self._deltaT_o(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.sbpgo * _kronecker_delta(i, j)) for j in range(nvar[2]): for k in range(offset, nvar[3]): jo = j + offset # skipping the T 0 variable if it exists for jj in range(nvar[3]): - symbolic_array_dic[(self._deltaT_o(i), self._psi_o(j), self._deltaT_o(k))] -= U_inv[i, jj] * bips.O(jj, jo, k) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(k)), - U_inv[i, jj] * bips.O(jj, jo, k)) # deltaT_g part if ground_temp: for i in range(nvar[2]): for jj in range(nvar[1]): for kk in range(nvar[2]): - symbolic_array_dic[(self._deltaT_g(i), 0, 0)] += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj]) for j in range(nvar[1]): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips.W(jj, j) - symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * 2 * symbolic_params['sc'] * self.Lpgo + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * symbolic_params['sc'] * self.Lpgo) if self.sbpa is not None: - symbolic_array_dic[(self._deltaT_g(i), self._theta_a(j), 0)] += val * self.sbpa + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.sbpa) for j in range(nvar[2]): - symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] = - self.Lpgo * _kronecker_delta(i, j) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) if self.sbpgo is not None: - symbolic_array_dic[(self._deltaT_g(i), self._deltaT_g(j), 0)] += - self.sbpgo * _kronecker_delta(i, j) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.sbpgo * _kronecker_delta(i, j)) - return symbolic_array_dic + return sy_arr_dic def compute_tensor(self): """Routine to compute the tensor.""" @@ -1048,6 +1048,13 @@ def _kronecker_delta(i, j): else: return 0 + +def _add_to_dict(dic, loc, value): + try: + dic[loc] += value + except: + dic[loc] = value + return dic def _symbolic_tensordot(a, b, axes=2): """ @@ -1081,4 +1088,9 @@ def _symbolic_tensordot(a, b, axes=2): prod = sy.tensorproduct(a, b) return sy.tensorcontraction(prod, sum_cols) - \ No newline at end of file + +if __name__ == "__main__": + dic = dict() + dic = _add_to_dict(dic, (0, 0), 1) + dic = _add_to_dict(dic, (0, 0), 2) + print(dic) \ No newline at end of file From 7f3728e0b4280afe8b257e5d61c1b5f41ec68146 Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 19 Jul 2023 17:44:20 +0200 Subject: [PATCH 014/143] using tensorproduct in all calculations --- qgs/tensors/symbolic_qgtensor.py | 165 ++++++++++++++++--------------- 1 file changed, 88 insertions(+), 77 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 10b22d7..c633376 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -306,9 +306,18 @@ def _compute_tensor_dicts(self): if aips.stored and go: # psi_a part - a_inv_mult_c = a_inv @ aips._c[offset:, offset:] - a_inv_mult_g = a_inv @ aips._g[offset:, :, offset:] - + a_inv_mult_c = _symbolic_tensordot(a_inv, aips._c[offset:, offset:], axes=1) + if gp.hk is not None: + a_inv_mult_g = _symbolic_tensordot(a_inv, aips._g[offset:, offset:, offset:], axes=1) + oro = _symbolic_tensordot(a_inv_mult_g, symbolic_params['hk'], axes=1) + else: + a_inv_mult_gh = _symbolic_tensordot(a_inv, aips._gh[offset:, offset:, offset:], axes=1) + oro = _symbolic_tensordot(a_inv_mult_gh, symbolic_params['hk'], axes=1) + + a_inv_mult_b = _symbolic_tensordot(a_inv, aips._b[offset:, offset:, offset:], axes=1) + a_inv_mult_d = a_inv @ aips._d[offset:, offset:] + + for i in range(nvar[0]): for j in range(nvar[0]): @@ -321,133 +330,135 @@ def _compute_tensor_dicts(self): if gp is not None: # convert if gp.hk is not None: - #//TODO: Can this be more efficient, make these slice matricies outside of the i loop? - if gp.orographic_basis == "atmospheric": - _g_slice = sy.matrices.immutable.ImmutableSparseMatrix(aips._g[offset:, jo, offset:]) - oro = a_inv @ _g_slice @ symbolic_params['hk'] # not perfect - else: - #//TODO: Can this be more efficient, make these slice matricies outside of the i loop? - # TODO: Can only be used with symbolic inner products here - a warning or an error should be raised if this is not the case. - _gh_slice = sy.matrices.immutable.ImmutableSparseMatrix(aips._gh[offset:, jo, offset:]) - oro = a_inv @ _gh_slice @ symbolic_params['hk'] # not perfect - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), oro[i] / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), oro[i, j] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j] / 2) - _b_slice = sy.matrices.immutable.ImmutableSparseMatrix(aips._b[offset:, jo, :offset]) - val = a_inv @ _b_slice for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), self._psi_a(k)), -val[i, k]) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), self._theta_a(ko)), -val[i, k]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), self._psi_a(k)), -a_inv_mult_b[i, j, k]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), self._theta_a(ko)), -a_inv_mult_b[i, j, k]) if ocean: - a_inv_mult_d = a_inv @ aips._d[offset:, offset:] for j in range(nvar[2]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * symbolic_params['kd'] / 2) # theta_a part + a_theta_mult_u = _symbolic_tensordot(a_theta, aips._u, axes=1) + if self.Cpa is not None: + val_Cpa = _symbolic_tensordot(a_theta_mult_u , self.Cpa, axes=1) + + if atp.hd is not None and self.thetas is not None: + val_thetas = _symbolic_tensordot(a_theta_mult_u, self.thetas, axes=1) # not perfect + + a_theta_mult_a = _symbolic_tensordot(a_theta, aips._a[:, offset:], axes=1) + a_theta_mult_c = _symbolic_tensordot(a_theta, aips._c[:, offset:], axes=1) + + if gp.orographic_basis == "atmospheric": + a_theta_mult_g = _symbolic_tensordot(a_theta, aips._g[:, offset:, offset:], axes=1) + oro = _symbolic_tensordot(a_theta_mult_g, symbolic_params['hk'], axes=1) + else: + a_theta_mult_gh = _symbolic_tensordot(a_theta, aips._gh[:, offset:, offset:], axes=1) + oro = _symbolic_tensordot(a_theta_mult_gh, symbolic_params['hk'], axes=1) + + a_theta_mult_b = _symbolic_tensordot(a_theta, aips._b[:, offset:, offset:], axes=1) + + if ocean or ground_temp: + a_theta_mult_d = _symbolic_tensordot(a_theta, aips._d[:, offset:], axes=1) + a_theta_mult_s = _symbolic_tensordot(a_theta, aips._s, axes=1) + for i in range(nvar[1]): if self.Cpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -a_theta[i, :] * aips._u * self.Cpa) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i]) if atp.hd is not None and atp.thetas is not None: - val = - a_theta[i, :] * aips._u * sp.COO(atp.thetas.astype(float)) # not perfect - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val * symbolic_params['hd']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_thetas[i] * symbolic_params['hd']) for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - val = a_theta[i, :] @ aips._a[:, jo] - val_2 = val * symbolic_params['kd'] * self.sig0 / 2 - val_3 = val * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 + val_2 = a_theta_mult_a[i, j] * symbolic_params['kd'] * self.sig0 / 2 + val_3 = a_theta_mult_a[i, j] * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val_2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -val_3) - val = - a_theta[i, :] @ aips._c[:, jo] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), val * symbolic_params['beta'] * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), a_theta_mult_c[i, j] * symbolic_params['beta'] * self.sig0) if gp is not None: if gp.hk is not None: #//TODO: Need to make this symbolic - if gp.orographic_basis == "atmospheric": - oro = a_theta[i, :] @ aips._g[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect - else: - oro = a_theta[i, :] @ aips._gh[:, jo, offset:] @ sp.COO(gp.hk.astype(float)) # not perfect - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -self.sig0 * oro / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -self.sig0 * oro[i, j] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro[i, j] / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists - val = a_theta[i, :] @ aips._b[:, jo, ko] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - val * self.sig0) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - val * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - a_theta_mult_b[i, j, k] * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - a_theta_mult_b[i, j, k] * self.sig0) - val = a_theta[i, :] @ aips._g[:, jo, ko] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), a_theta_mult_g[i, j, k]) for j in range(nvar[1]): - val = a_theta[i, :] @ aips._u[:, j] if self.Lpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * symbolic_params['sc'] * self.Lpa) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * symbolic_params['sc'] * self.Lpa) if self.LSBpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.LSBpa) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * self.LSBpa) if atp.hd is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.hd) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.hd) if ocean: for j in range(nvar[2]): - jo = j + offset # skipping the theta 0 variable if it exists - val = - a_theta[i, :] @ aips._d[:, jo] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * self.sig0 * symbolic_params['kd'] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), a_theta_mult_d[i, j] * self.sig0 * symbolic_params['kd'] / 2) if self.Lpa is not None: for j in range(nvar[3]): - val = - a_theta[i, :] @ aips._s[:, j] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.Lpa / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.Lpa / 2) if self.LSBpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.LSBpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.LSBpgo) if ground_temp: if self.Lpa is not None: for j in range(nvar[2]): - val = - a_theta[i, :] @ aips._s[:, j] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.Lpa / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.Lpa / 2) if self.LSBpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.LSBpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.LSBpgo) if ocean: # psi_o part + M_psio_mult_K = _symbolic_tensordot(M_psio, bips._K[offset:, offset:], axes=1) + M_psio_mult_N = _symbolic_tensordot(M_psio, bips._N[offset:, offset:], axes=1) + M_psio_mult_M = _symbolic_tensordot(M_psio, bips._M[offset:, offset:], axes=1) + M_psio_mult_C = _symbolic_tensordot(M_psio, bips._C[offset:, offset:, offset:], axes=1) + for i in range(nvar[2]): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - val = M_psio[i, :] @ bips._K[offset:, jo] * symbolic_params['d'] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), val) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), val) + + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), M_psio_mult_K[i, j] * symbolic_params['d']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), M_psio_mult_K[i, j] * symbolic_params['d']) for j in range(nvar[2]): - jo = j + offset # skipping the T 0 variable if it exists - val = - M_psio[i, :] @ bips._N[offset:, jo] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * symbolic_params['beta']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_N[i, j] * symbolic_params['beta']) - val = - M_psio[i, :] @ bips._M[offset:, jo] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * (symbolic_params['r'] + symbolic_params['d'])) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_M[i, j] * (symbolic_params['r'] + symbolic_params['d'])) for k in range(nvar[2]): - ko = k + offset # skipping the T 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio[i, :] @ bips._C[offset:, jo, ko]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio_mult_C[i, j, k]) # deltaT_o part + U_inv_mult_W = _symbolic_tensordot(U_inv, bips._W, axes=1) + U_inv_mult_W_Cpgo = _symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) + + U_inv_mult_O = _symbolic_tensordot(U_inv, bips._O[offset:, offset:, offset:], axes=1) + for i in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv[i, :] @ bips._W @ sp.COO(self.Cpgo)) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i]) for j in range(nvar[1]): - val = U_inv[i, :] @ bips._W[:, j] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * symbolic_params['sc'] * self.Lpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * symbolic_params['sc'] * self.Lpgo) if self.sbpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * self.sbpa) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) for j in range(nvar[3]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) @@ -456,20 +467,20 @@ def _compute_tensor_dicts(self): for j in range(nvar[2]): for k in range(nvar[2]): - jo = j + offset # skipping the T 0 variable if it exists ko = k + offset # skipping the T 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(ko)), -U_inv[i, :] @ bips._O[:, jo, ko]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(ko)), -U_inv_mult_O[i, j, k]) # deltaT_g part if ground_temp: + U_inv_mult_W = _symbolic_tensordot(U_inv, bips._W, axes=1) + U_inv_mult_W_Cpgo = _symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) for i in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv[i, :] @ bips._W @ sp.COO(self.Cpgo)) # not perfect + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i]) # not perfect for j in range(nvar[1]): - val = U_inv[i, :] @ bips._W[:, j] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * symbolic_params['sc'] * self.Lpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * symbolic_params['sc'] * self.Lpgo) if self.sbpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.sbpa) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) for j in range(nvar[2]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) @@ -488,7 +499,7 @@ def _compute_tensor_dicts(self): val += a_inv[i, jj] * aips.c(offset + jj, jo) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - val * symbolic_params['beta']) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd * _kronecker_delta(i, j)) / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) #//TODO: what is gp.hk parameter??? if gp is not None: @@ -497,11 +508,11 @@ def _compute_tensor_dicts(self): if gp.orographic_basis == "atmospheric": for jj in range(nvar[0]): for kk in range(nvar[0]): - oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * self.hk[kk] + oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * symbolic_params['kd'] else: for jj in range(nvar[0]): for kk in range(nvar[0]): - oro += a_inv[i, jj] * aips.gh(offset + jj, j, offset + kk) * gp.hk[kk] + oro += a_inv[i, jj] * aips.gh(offset + jj, j, offset + kk) * symbolic_params['hk_val'] sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - oro / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro / 2) @@ -518,7 +529,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.d(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), val * ap.kd / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), val * symbolic_params['kd'] / 2) # theta_a part @@ -556,11 +567,11 @@ def _compute_tensor_dicts(self): if gp.orographic_basis == "atmospheric": for jj in range(nvar[1]): for kk in range(nvar[0]): - oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * self.hk[kk] + oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * symbolic_params['hk'][kk] else: for jj in range(nvar[1]): for kk in range(nvar[0]): - oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * self.hk[kk] + oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * symbolic_params['hk'][kk] sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - self.sig0 * oro / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro / 2) From 7d583eed340789c0f815ab2047e1fa8b0fb71de8 Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 19 Jul 2023 20:48:38 +0200 Subject: [PATCH 015/143] Rough draft of dynT --- qgs/tensors/symbolic_qgtensor.py | 212 +++++++++++++++++-------------- 1 file changed, 120 insertions(+), 92 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index c633376..eb9012f 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -64,6 +64,7 @@ def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_p self.jacobian_tensor = None self.params.symbolic_insolation_array() + self.params.symbolic_orography_array() # self.compute_tensor() @@ -181,7 +182,7 @@ def sbpgo(self): @property def sbpa(self): - return 8 * self.sym_params['eps'] * self.param.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + return 8 * self.sym_params['eps'] * self.params.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) @property def LSBpgo(self): @@ -222,12 +223,8 @@ def _compute_tensor_dicts(self): par = self.params symbolic_params = self.sym_params atp = par.atemperature_params - ap = par.atmospheric_params - op = par.oceanic_params - scp = par.scale_params gp = par.ground_params nvar = par.number_of_variables - ndim = par.ndim bips = None if self.oceanic_inner_products is not None: @@ -307,7 +304,7 @@ def _compute_tensor_dicts(self): if aips.stored and go: # psi_a part a_inv_mult_c = _symbolic_tensordot(a_inv, aips._c[offset:, offset:], axes=1) - if gp.hk is not None: + if symbolic_params['hk'] is not None: a_inv_mult_g = _symbolic_tensordot(a_inv, aips._g[offset:, offset:, offset:], axes=1) oro = _symbolic_tensordot(a_inv_mult_g, symbolic_params['hk'], axes=1) else: @@ -329,10 +326,10 @@ def _compute_tensor_dicts(self): if gp is not None: # convert - if gp.hk is not None: + if symbolic_params['hk'] is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), oro[i, j] / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), oro[i, j][0] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j][0] / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists @@ -348,18 +345,20 @@ def _compute_tensor_dicts(self): if self.Cpa is not None: val_Cpa = _symbolic_tensordot(a_theta_mult_u , self.Cpa, axes=1) - if atp.hd is not None and self.thetas is not None: - val_thetas = _symbolic_tensordot(a_theta_mult_u, self.thetas, axes=1) # not perfect + if symbolic_params['hd'] is not None and symbolic_params['thetas'] is not None: + val_thetas = _symbolic_tensordot(a_theta_mult_u, symbolic_params['thetas'], axes=1) # not perfect a_theta_mult_a = _symbolic_tensordot(a_theta, aips._a[:, offset:], axes=1) a_theta_mult_c = _symbolic_tensordot(a_theta, aips._c[:, offset:], axes=1) + + a_theta_mult_g = _symbolic_tensordot(a_theta, aips._g[:, offset:, offset:], axes=1) - if gp.orographic_basis == "atmospheric": - a_theta_mult_g = _symbolic_tensordot(a_theta, aips._g[:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_theta_mult_g, symbolic_params['hk'], axes=1) - else: - a_theta_mult_gh = _symbolic_tensordot(a_theta, aips._gh[:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_theta_mult_gh, symbolic_params['hk'], axes=1) + if gp is not None: + if gp.orographic_basis == "atmospheric": + oro = _symbolic_tensordot(a_theta_mult_g, symbolic_params['hk'], axes=1) + else: + a_theta_mult_gh = _symbolic_tensordot(a_theta, aips._gh[:, offset:, offset:], axes=1) + oro = _symbolic_tensordot(a_theta_mult_gh, symbolic_params['hk'], axes=1) a_theta_mult_b = _symbolic_tensordot(a_theta, aips._b[:, offset:, offset:], axes=1) @@ -371,22 +370,22 @@ def _compute_tensor_dicts(self): if self.Cpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i]) - if atp.hd is not None and atp.thetas is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_thetas[i] * symbolic_params['hd']) + if symbolic_params['hd'] is not None and atp.thetas is not None: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_thetas[i][0] * symbolic_params['hd']) for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists val_2 = a_theta_mult_a[i, j] * symbolic_params['kd'] * self.sig0 / 2 - val_3 = a_theta_mult_a[i, j] * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 + val_3 = a_theta_mult_a[i, j] * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kpd']) * self.sig0 sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val_2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -val_3) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), a_theta_mult_c[i, j] * symbolic_params['beta'] * self.sig0) if gp is not None: - if gp.hk is not None: + if symbolic_params['hk'] is not None: #//TODO: Need to make this symbolic sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -self.sig0 * oro[i, j] / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro[i, j] / 2) @@ -453,7 +452,7 @@ def _compute_tensor_dicts(self): U_inv_mult_O = _symbolic_tensordot(U_inv, bips._O[offset:, offset:, offset:], axes=1) for i in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i][0]) for j in range(nvar[1]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * symbolic_params['sc'] * self.Lpgo) @@ -707,16 +706,10 @@ def _compute_tensor_dicts(self): def compute_tensor(self): """Routine to compute the tensor.""" - # gathering - par = self.params - ndim = par.ndim - - sparse_arrays_dict = self._compute_tensor_dicts() - tensor = sp.zeros((ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='coo') - if sparse_arrays_dict is not None: - tensor = self._add_dict_to_tensor(sparse_arrays_dict, tensor) - self._set_tensor(tensor) + sy_arr_dic = self._compute_tensor_dicts() + sy_arr_dic = _remove_dic_zeros(sy_arr_dic) + return sy_arr_dic def _set_tensor(self, tensor): if not isinstance(tensor, sp.COO): @@ -844,59 +837,66 @@ def _compute_stored_full_dict(self): ################# - symbolic_array_full_dict = dict() + sy_arr_dic = dict() # theta_a part + a_theta_mult_z = _symbolic_tensordot(a_theta, aips._z, axes=1) + a_theta_mult_v = _symbolic_tensordot(a_theta, aips._v, axes=1) + for i in range(nvar[1]): - # symbolic_array_full_dict[self._theta_a(i)] = list() if self.T4LSBpa is not None: - # val = sy.physics.quantum.tensorproduct.TensorProduct((a_theta[i], aips._z)) - # val = sp.tensordot(a_theta[i], aips._z, axes=1) - if val.nnz > 0: - symbolic_array_full_dict[self._theta_a(i)].append(self._shift_tensor_coordinates(self.T4LSBpa * val, self._theta_a(0))) + j = k = ell = 0 + for m in range(nvar[1]): + val = self.T4LSBpa * a_theta_mult_z[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), val) if ocean: - if par.T4LSBpgo is not None: - val = sp.tensordot(a_theta[i], aips._v, axes=1) - if val.nnz > 0: - symbolic_array_full_dict[self._theta_a(i)].append(self._shift_tensor_coordinates(- self.T4LSBpgo * val, self._deltaT_o(0))) + if self.T4LSBpgo is not None: + j = k = ell = 0 + for m in range(nvar[3]): + val = self.T4LSBpgo * a_theta_mult_v[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), val) if ground_temp: - - if par.T4LSBpgo is not None: - val = sp.tensordot(a_theta[i], aips._v, axes=1) - if val.nnz > 0: - symbolic_array_full_dict[self._theta_a(i)].append(self._shift_tensor_coordinates(- self.T4LSBpgo * val, self._deltaT_g(0))) + if self.T4LSBpgo is not None: + j = k = ell = 0 + for m in range(nvar[2]): + val = self.T4LSBpgo * a_theta_mult_v[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), val) if ocean: # deltaT_o part + U_inv_mult_Z = _symbolic_tensordot(U_inv, bips._Z, axes=1) + U_inv_mult_V = _symbolic_tensordot(U_inv, bips._V, axes=1) + for i in range(nvar[3]): - symbolic_array_full_dict[self._deltaT_o(i)] = list() - val = sp.tensordot(U_inv[i], bips._Z, axes=1) - if val.nnz > 0: - symbolic_array_full_dict[self._deltaT_o(i)].append(self._shift_tensor_coordinates(self.T4sbpa * val, self._theta_a(0))) - val = sp.tensordot(U_inv[i], bips._V, axes=1) - if val.nnz > 0: - symbolic_array_full_dict[self._deltaT_o(i)].append(self._shift_tensor_coordinates(- self.T4sbpgo * val, self._deltaT_o(0))) + j = k = ell = 0 + for m in range(nvar[1]): + val = self.T4sbpa * U_inv_mult_Z[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), val) + + val = - self.T4sbpgo * U_inv_mult_V[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), val) if ground_temp: # deltaT_g part + U_inv_mult_Z = _symbolic_tensordot(U_inv, bips._Z, axes=1) + U_inv_mult_V = _symbolic_tensordot(U_inv, bips._V, axes=1) + for i in range(nvar[2]): - symbolic_array_full_dict[self._deltaT_g(i)] = list() - val = sp.tensordot(U_inv[i], bips._Z, axes=1) - if val.nnz > 0: - symbolic_array_full_dict[self._deltaT_g(i)].append(self._shift_tensor_coordinates(self.T4sbpa * val, self._theta_a(0))) - val = sp.tensordot(U_inv[i], bips._V, axes=1) - if val.nnz > 0: - symbolic_array_full_dict[self._deltaT_g(i)].append(self._shift_tensor_coordinates(- self.T4sbpgo * val, self._deltaT_g(0))) + j = k = ell = 0 + for m in range(nvar[1]): + val = self.T4sbpa * U_inv_mult_Z[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), val) - return symbolic_array_full_dict + val = - self.T4sbpgo * U_inv_mult_V[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), val) + + return sy_arr_dic def _compute_non_stored_full_dict(self): par = self.params - ap = par.atmospheric_params nvar = par.number_of_variables - ndim = par.ndim aips = self.atmospheric_inner_products bips = None @@ -914,13 +914,15 @@ def _compute_non_stored_full_dict(self): # constructing some derived matrices if aips is not None: - + a_theta = dict() a_theta = np.zeros((nvar[1], nvar[1])) for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[i, j] = self.sig0 * aips.a(i, j) - aips.u(i, j) - a_theta = np.linalg.inv(a_theta) - a_theta = sp.COO(a_theta) + a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) + + a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) + a_theta = a_theta.inverse() + a_theta = a_theta.simplify() if bips is not None: if ocean: @@ -938,11 +940,12 @@ def _compute_non_stored_full_dict(self): U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) U_inv = U_inv.inverse() + U_inv = U_inv.simplify() ################# - symbolic_array_full_dict = dict() + sy_arr_dic = dict() # theta_a part for i in range(nvar[1]): # t_full = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='dok') @@ -954,9 +957,9 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips.z(jj, j, k, ell, m) if m == 0: - symbolic_array_full_dict[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4LSBpa * val + sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4LSBpa * val else: - symbolic_array_full_dict[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4LSBpa * val + sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4LSBpa * val if ocean: if self.T4LSBpgo is not None: @@ -966,9 +969,9 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) if m == 0: - symbolic_array_full_dict[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4LSBpgo * val else: - symbolic_array_full_dict[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4LSBpgo * val if ground_temp: if self.T4LSBpgo is not None: @@ -978,9 +981,9 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) if m == 0: - symbolic_array_full_dict[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4LSBpgo * val else: - symbolic_array_full_dict[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.T4LSBpgo * val if ocean: @@ -995,18 +998,18 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips.Z(jj, j, k, ell, m) if m == 0: - symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val else: - symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4sbpa * val + sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4sbpa * val for m in range(nvar[3]): val = 0 for jj in range(nvar[3]): val -= U_inv[i, jj] * bips.V(jj, j, k, ell, m) if m == 0: - symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4sbpgo * val + sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4sbpgo * val else: - symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4sbpgo * val + sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4sbpgo * val # deltaT_g part if ground_temp: @@ -1020,36 +1023,35 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val else: - symbolic_array_full_dict[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4sbpa * val + sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4sbpa * val for m in range(nvar[2]): val = 0 for jj in range(nvar[2]): val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4sbpgo * val + sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4sbpgo * val else: - symbolic_array_full_dict[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.T4sbpgo * val + sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.T4sbpgo * val - return symbolic_array_full_dict + return sy_arr_dic def compute_tensor(self): """Routine to compute the tensor.""" # gathering par = self.params - ndim = par.ndim - sparse_arrays_dict = SymbolicTensorLinear._compute_tensor_dicts(self) - symbolic_array_full_dict = self._compute_tensor_dicts() + symbolic_dict_linear = SymbolicTensorLinear.compute_tensor(self) + symbolic_dict_linear = _shift_dict_keys(symbolic_dict_linear, (0, 0)) - tensor = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='coo') - if sparse_arrays_dict is not None: - tensor = self._add_dict_to_tensor(sparse_arrays_dict, tensor) - if symbolic_array_full_dict is not None: - tensor = self._add_dict_to_tensor(symbolic_array_full_dict, tensor) - self._set_tensor(tensor) + symbolic_dict_dynT = self._compute_tensor_dicts(self) + + if symbolic_dict_linear is not None: + symbolic_dict_dynT = {**symbolic_dict_linear, **symbolic_dict_dynT} + + return symbolic_dict_dynT def _kronecker_delta(i, j): @@ -1067,6 +1069,32 @@ def _add_to_dict(dic, loc, value): dic[loc] = value return dic +def _remove_dic_zeros(dic): + non_zero_dic = dict() + for key in dic.keys(): + if dic[key] != 0: + non_zero_dic[key] = dic[key] + + return non_zero_dic + +def _shift_dict_keys(dic, shift): + """ + Keys of given dictionary are altered to add values in the given indicies + + Parameters + ---------- + dic: dictionary + + shift: Tuple + """ + + shifted_dic = dict() + for key in dic.keys(): + new_key = key + shift + shifted_dic[new_key] = dic[key] + + return shifted_dic + def _symbolic_tensordot(a, b, axes=2): """ Compute tensor dot product along specified axes of two sympy symbolic arrays From f64422b5d036de9f4f69bd629fd8dbb70ac5b81b Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 20 Jul 2023 13:32:04 +0200 Subject: [PATCH 016/143] Addition of useful functions --- qgs/tensors/symbolic_qgtensor.py | 91 ++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 16 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index eb9012f..c7967d7 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -708,16 +708,83 @@ def compute_tensor(self): """Routine to compute the tensor.""" sy_arr_dic = self._compute_tensor_dicts() - sy_arr_dic = _remove_dic_zeros(sy_arr_dic) - return sy_arr_dic + sy_arr_dic = self.remove_dic_zeros(sy_arr_dic) + + if sy_arr_dic is not None: + if not(self.params.dynamic_T): + self._set_tensor(sy_arr_dic) + + def _set_tensor(self, dic): + ndim = self.params.ndim + + jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jacobian_from_dict(dic), (ndim + 1, ndim + 1, ndim + 1)) + tensor = sy.tensor.array.ImmutableSparseNDimArray(self.simplify_dict(dic), (ndim + 1, ndim + 1, ndim + 1)) + + self.jacobian_tensor = jacobian_tensor.simplify() + self.tensor = tensor.simplify() + + @staticmethod + def remove_dic_zeros(dic): + non_zero_dic = dict() + for key in dic.keys(): + if dic[key] != 0: + non_zero_dic[key] = dic[key] + + return non_zero_dic + + @staticmethod + def jacobian_from_dict(dic): + rank = max([len(i) for i in dic.keys()]) + n_perm = rank - 2 + + orig_order = [i for i in range(rank)] + + keys = dic.keys() + dic_jac = dic.copy() + + for i in range(1, n_perm+1): + new_pos = orig_order.copy() + new_pos[1] = orig_order[i+1] + new_pos[i+1] = orig_order[1] + for key in keys: - def _set_tensor(self, tensor): - if not isinstance(tensor, sp.COO): - tensor = tensor.to_coo() - self.jacobian_tensor = self.jacobian_from_tensor(tensor) - self.tensor = self.simplify_tensor(tensor) + dic_jac = _add_to_dict(dic_jac, tuple(key[i] for i in new_pos), dic[key]) + + return dic_jac + + @staticmethod + def simplify_dict(dic): + keys = dic.keys() + dic_upp = dic.copy() + + for key in keys: + new_key = tuple(sorted(key)) + dic_upp = _add_to_dict(dic_upp, new_key, dic[key]) - #//TODO: include other functions for altering dictionary. + return dic_upp + + def save_to_file(self, filename, **kwargs): + """Function to save the tensor object to a file with the :mod:`pickle` module. + + Parameters + ---------- + filename: str + The file name where to save the tensor object. + kwargs: dict + Keyword arguments to pass to the :mod:`pickle` module method. + """ + f = open(filename, 'wb') + pickle.dump(self.__dict__, f, **kwargs) + f.close() + + @staticmethod + def print_tensor(tensor): + nx, ny, nz = tensor.shape + for i in range(nx): + for j in range(ny): + for k in range(nz): + if tensor[i, j, k] != 0: + print("("+str(i, j, k) + "): " + str(tensor[i, j, k])) class SymbolicTensorDynamicT(SymbolicTensorLinear): #//TODO: Need to work out symbolic tensor dot @@ -1069,14 +1136,6 @@ def _add_to_dict(dic, loc, value): dic[loc] = value return dic -def _remove_dic_zeros(dic): - non_zero_dic = dict() - for key in dic.keys(): - if dic[key] != 0: - non_zero_dic[key] = dic[key] - - return non_zero_dic - def _shift_dict_keys(dic, shift): """ Keys of given dictionary are altered to add values in the given indicies From b600ad5e71d700e5443d915d684278764931bd18 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 20 Jul 2023 17:35:01 +0200 Subject: [PATCH 017/143] Changing the way subs work in IP --- qgs/inner_products/symbolic.py | 76 +++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 08f4016..2668aaa 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -175,7 +175,7 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.return_symbolic = return_symbolic if return_symbolic: self._p_compute = _symbolic_compute - self.subs = [('n', params.symbolic_params['n'])] + self.subs = None #[('n', params.symbolic_params['n'])] else: self._p_compute = _parallel_compute self.subs = [('n', self.n)] @@ -240,8 +240,10 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - - subs = self.subs + self.atmospheric_basis.substitutions + self.oceanic_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions + self.oceanic_basis.substitutions noc = len(ocean_basis) self._gh = None @@ -499,12 +501,18 @@ def _integrate(self, subs, args): return res[1] else: res = _apply(args)[1] - return float(res.subs(subs)) + if subs is not None: + return float(res.subs(subs)) + else: + return res def a(self, i, j): """Function to compute the matrix of the eigenvalues of the Laplacian (atmospheric): :math:`a_{i, j} = (F_i, {\\nabla}^2 F_j)`.""" if not self.stored: - subs = self.subs + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions args = ((i, j), self.ip.ip_lap, (self._F(i), self._F(j)), subs) return self._integrate(subs, args) else: @@ -513,7 +521,10 @@ def a(self, i, j): def u(self, i, j): """Function to compute the matrix of inner product: :math:`u_{i, j} = (F_i, F_j)`.""" if not self.stored: - subs = self.subs + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions args = ((i, j), self.ip.symbolic_inner_product, (self._F(i), self._F(j)), subs) return self._integrate(subs, args) else: @@ -522,7 +533,10 @@ def u(self, i, j): def b(self, i, j, k): """Function to compute the tensors holding the Jacobian inner products: :math:`b_{i, j, k} = (F_i, J(F_j, \\nabla^2 F_k))`.""" if not self.stored: - subs = self.subs + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions args = ((i, j, k), self.ip.ip_jac_lap, (self._F(i), self._F(j), self._F(k)), subs) return self._integrate(subs, args) else: @@ -531,7 +545,11 @@ def b(self, i, j, k): def c(self, i, j): """Function to compute the matrix of beta terms for the atmosphere: :math:`c_{i,j} = (F_i, \\partial_x F_j)`.""" if not self.stored: - subs = self.subs + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions + print(subs) args = ((i, j), self.ip.ip_diff_x, (self._F(i), self._F(j)), subs) return self._integrate(subs, args) else: @@ -540,7 +558,10 @@ def c(self, i, j): def g(self, i, j, k): """Function to compute tensors holding the Jacobian inner products: :math:`g_{i,j,k} = (F_i, J(F_j, F_k))`.""" if not self.stored: - subs = self.subs + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions args = ((i, j, k), self.ip.ip_jac, (self._F(i), self._F(j), self._F(k)), subs) return self._integrate(subs, args) else: @@ -560,7 +581,10 @@ def gh(self, i, j, k): else: extra_subs = None - subs = self.subs + self.atmospheric_basis.substitutions + extra_subs + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions + extra_subs args = ((i, j, k), self.iip.ip_jac, (self._F(i), self._F(j), self._phi(k)), subs) return self._integrate(subs, args) else: @@ -579,7 +603,10 @@ def s(self, i, j): else: extra_subs = None - subs = self.subs + self.atmospheric_basis.substitutions + extra_subs + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions + extra_subs args = ((i, j), self.iip.symbolic_inner_product, (self._F(i), self._phi(j)), subs) return self._integrate(subs, args) else: @@ -598,7 +625,10 @@ def d(self, i, j): else: extra_subs = None - subs = self.subs + self.atmospheric_basis.substitutions + extra_subs + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions + extra_subs args = ((i, j), self.iip.ip_lap, (self._F(i), self._phi(j)), subs) return self._integrate(subs, args) else: @@ -607,7 +637,11 @@ def d(self, i, j): def z(self, i, j, k, l, m): """Function to compute the :math:`T^4` temperature forcing for the radiation lost by atmosphere to space & ground/ocean: :math:`z_{i,j,k,l,m} = (F_i, F_j F_k F_l F_m)`.""" if not self.stored: - subs = self.subs + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions + args = ((i, j, k, l, m), self.ip.symbolic_inner_product, (self._F(i), self._F(j) * self._F(k) * self._F(l) * self._F(m)), subs) if self.quadrature: res = _num_apply(args) @@ -621,7 +655,10 @@ def z(self, i, j, k, l, m): def v(self, i, j, k, l, m): """Function to compute the :math:`T^4` temperature forcing of the ocean on the atmosphere: :math:`v_{i,j,k,l,m} = (F_i, \\phi_j \\phi_k \\phi_l \\phi_m)`.""" if not self.stored: - subs = self.subs + self.atmospheric_basis.substitutions + if self.return_symbolic: + subs = self.subs + else: + subs = self.subs + self.atmospheric_basis.substitutions args = ((i, j, k, l, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(j) * self._phi(k) * self._phi(l) * self._phi(m)), subs) if self.quadrature: res = _num_apply(args) @@ -1437,7 +1474,11 @@ def _apply(ls): def _num_apply(ls): integrand = ls[1](*ls[2], integrand=True) - num_integrand = integrand[0].subs(ls[3]) + if ls[3] is not None: + num_integrand = integrand[0].subs(ls[3]) + else: + num_integrand = integrand[0] + func = sy.lambdify((integrand[1][0], integrand[2][0]), num_integrand, 'numpy') try: @@ -1536,7 +1577,10 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False while True: try: res = next(results) - result_list.append(res[1].subs(subs)) + if subs is not None: + result_list.append(res[1].subs(subs)) + else: + result_list.append(res[1]) except StopIteration: break From cb29fd4cfcd46beb5ca3ec93886433a927e6a36f Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 20 Jul 2023 17:35:32 +0200 Subject: [PATCH 018/143] Edits to the way printing of the tensor works --- qgs/tensors/symbolic_qgtensor.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index c7967d7..b8d78bd 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -319,7 +319,6 @@ def _compute_tensor_dicts(self): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - #//TODO: A =- was converted to = here, I need to make sure this doesnt alter the results sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), a_inv_mult_c[i, j] * symbolic_params['beta']) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -(symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) @@ -368,7 +367,7 @@ def _compute_tensor_dicts(self): for i in range(nvar[1]): if self.Cpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i][0]) if symbolic_params['hd'] is not None and atp.thetas is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_thetas[i][0] * symbolic_params['hd']) @@ -387,8 +386,8 @@ def _compute_tensor_dicts(self): if gp is not None: if symbolic_params['hk'] is not None: #//TODO: Need to make this symbolic - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -self.sig0 * oro[i, j] / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro[i, j] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -self.sig0 * oro[i, j][0] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro[i, j][0] / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists @@ -719,9 +718,8 @@ def _set_tensor(self, dic): jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jacobian_from_dict(dic), (ndim + 1, ndim + 1, ndim + 1)) tensor = sy.tensor.array.ImmutableSparseNDimArray(self.simplify_dict(dic), (ndim + 1, ndim + 1, ndim + 1)) - - self.jacobian_tensor = jacobian_tensor.simplify() - self.tensor = tensor.simplify() + self.jacobian_tensor = jacobian_tensor.applyfunc(sy.simplify) + self.tensor = tensor.applyfunc(sy.simplify) @staticmethod def remove_dic_zeros(dic): @@ -754,11 +752,12 @@ def jacobian_from_dict(dic): @staticmethod def simplify_dict(dic): + # this is not correct, I should not permute the tuple, but sort the list of them keys = dic.keys() - dic_upp = dic.copy() + dic_upp = dict() for key in keys: - new_key = tuple(sorted(key)) + new_key = tuple([key[0]] + sorted(key[1:])) dic_upp = _add_to_dict(dic_upp, new_key, dic[key]) return dic_upp @@ -777,14 +776,13 @@ def save_to_file(self, filename, **kwargs): pickle.dump(self.__dict__, f, **kwargs) f.close() - @staticmethod - def print_tensor(tensor): - nx, ny, nz = tensor.shape + def print_tensor(self): + nx, ny, nz = self.tensor.shape for i in range(nx): for j in range(ny): for k in range(nz): - if tensor[i, j, k] != 0: - print("("+str(i, j, k) + "): " + str(tensor[i, j, k])) + if self.tensor[i, j, k] != 0: + print(str((i, j, k)) + ": " + str(self.tensor[i, j, k])) class SymbolicTensorDynamicT(SymbolicTensorLinear): #//TODO: Need to work out symbolic tensor dot From 9b618f9ef3f057d4caa5153aa92d816f754e189f Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 21 Jul 2023 19:36:10 +0200 Subject: [PATCH 019/143] change to definition of n in basis --- qgs/basis/base.py | 5 +++-- qgs/basis/fourier.py | 2 +- qgs/params/params.py | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/qgs/basis/base.py b/qgs/basis/base.py index a9cb419..2807ac6 100644 --- a/qgs/basis/base.py +++ b/qgs/basis/base.py @@ -26,7 +26,7 @@ import sys from abc import ABC -from sympy import symbols, lambdify, diff +from sympy import Symbol, symbols, lambdify, diff class Basis(ABC): @@ -216,7 +216,8 @@ def num_functions(self): basis = SymbolicBasis() x, y = symbols('x y') # x and y coordinates on the model's spatial domain - n, al = symbols('n al') # aspect ratio and alpha coefficients + al = symbols('al') # aspect ratio and alpha coefficients + n = Symbol('n', positive=True) for i in range(1, 3): for j in range(1, 3): basis.append(2 * exp(- al * x) * sin(j * n * x / 2) * sin(i * y)) diff --git a/qgs/basis/fourier.py b/qgs/basis/fourier.py index f645d91..1a14794 100644 --- a/qgs/basis/fourier.py +++ b/qgs/basis/fourier.py @@ -21,7 +21,7 @@ from sympy import symbols, sin, cos, sqrt _x, _y = symbols('x y') -_n = symbols('n', real=True, nonnegative=True) +_n = symbols('n', positive=True) class ChannelFourierBasis(SymbolicBasis): diff --git a/qgs/params/params.py b/qgs/params/params.py index 4aa5f4c..3b634e6 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -865,6 +865,8 @@ class QgParams(Params): # Atmosphere Temp Parameters 'hd': sy.Symbol('hd'), 'theta': sy.Symbol('theta'), + #//TODO: Need to work out what thetas should be + 'thetas': None, 'atm_gamma': sy.Symbol('gamma_a'), 'atm_C_val': sy.Symbol('C_a'), 'atm_C': None, From 7e658f65ee65c1548dd67770b760cd1e77974c4b Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 24 Jul 2023 12:43:19 +0100 Subject: [PATCH 020/143] First working symbolic tensor - outputs incorrect4 --- qgs/inner_products/definition.py | 4 +- qgs/inner_products/symbolic.py | 2 +- qgs/params/params.py | 68 ++++++++++++++++++++++++++++- qgs/tensors/symbolic_qgtensor.py | 75 +++++++++++++++++++++++++++++--- 4 files changed, 139 insertions(+), 10 deletions(-) diff --git a/qgs/inner_products/definition.py b/qgs/inner_products/definition.py index 86187be..b8cf87c 100644 --- a/qgs/inner_products/definition.py +++ b/qgs/inner_products/definition.py @@ -13,9 +13,9 @@ # from sympy.simplify import trigsimp from sympy.simplify.fu import TR8, TR10 -from sympy import diff, integrate, symbols, pi, Integral +from sympy import Symbol, diff, integrate, symbols, pi, Integral -_n = symbols('n', real=True, nonnegative=True) +_n = Symbol('n', positive=True) _x, _y = symbols('x y') diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 2668aaa..e2a79e3 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -549,7 +549,7 @@ def c(self, i, j): subs = self.subs else: subs = self.subs + self.atmospheric_basis.substitutions - print(subs) + args = ((i, j), self.ip.ip_diff_x, (self._F(i), self._F(j)), subs) return self._integrate(subs, args) else: diff --git a/qgs/params/params.py b/qgs/params/params.py index 3b634e6..7ea42e7 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -859,7 +859,7 @@ class QgParams(Params): # Atmosphere Parameters 'kd': sy.Symbol('k_d'), - 'kpd': sy.Symbol('k_p'), + 'kdp': sy.Symbol('k_p'), 'sigma': sy.Symbol('sigma'), # Atmosphere Temp Parameters @@ -2073,3 +2073,69 @@ def _set_ground_analytic_fourier_modes(self, nxmax=None, nymax=None, auto=True): for i in range(self.nmod[1]): self._ground_latex_var_string.append(r'delta T_{\rm g,' + str(i + 1) + "}") self._ground_var_string.append(r'delta_T_g_' + str(i + 1)) + + + def _set_symbolic_parameters(self): + """ + Function to make a map between symbolic parameters and numberical values + """ + + #//TODO: THis function is a really lazy way of doing this, look into setting up the Parameter class with each symbol stored in the class + self.symbol_to_value = dict() + + # Scale Parameters + if self.scale_params is not None: + self.symbol_to_value['L'] = (self.symbolic_params['L'], self.scale_params.L) + self.symbol_to_value['fo'] = (self.symbolic_params['fo'], self.scale_params.f0) + self.symbol_to_value['beta'] = (self.symbolic_params['beta'], self.scale_params.beta) + self.symbol_to_value['n'] = (self.symbolic_params['n'], self.scale_params.n) + + # Atmosphere Parameters + if self.atmospheric_params is not None: + self.symbol_to_value['kd'] = (self.symbolic_params['kd'], self.atmospheric_params.kd) + self.symbol_to_value['kdp'] = (self.symbolic_params['kdp'], self.atmospheric_params.kdp) + self.symbol_to_value['sigma'] = (self.symbolic_params['sigma'], self.atmospheric_params.sigma) + + + #//TODO: Fix the bogde on the index of the insolation + # Atmosphere Temp Parameters + if self.atemperature_params is not None: + self.symbol_to_value['hd'] = (self.symbolic_params['hd'], self.atemperature_params.hd) + self.symbol_to_value['theta'] = (self.symbolic_params['theta'], self.atemperature_params.thetas) + self.symbol_to_value['thetas'] = (self.symbolic_params['theta'], self.atemperature_params.thetas) + self.symbol_to_value['atm_gamma'] = (self.symbolic_params['atm_gamma'], self.atemperature_params.gamma) + self.symbol_to_value['atm_C_val'] = (self.symbolic_params['atm_C_val'], self.atemperature_params.C[0]) + self.symbol_to_value['atm_C'] = (self.symbolic_params['atm_C_val'], self.atemperature_params.C[0]) + self.symbol_to_value['eps'] = (self.symbolic_params['eps'], self.atemperature_params.eps) + self.symbol_to_value['atm_T0'] = (self.symbolic_params['atm_T0'], self.atemperature_params.T0) + self.symbol_to_value['sc'] = (self.symbolic_params['sc'], self.atemperature_params.sc) + self.symbol_to_value['hlambda'] = (self.symbolic_params['hlambda'], self.atemperature_params.hlambda) + + # Ground Parameters + if self.ground_params is not None: + self.symbol_to_value['hk_val'] = (self.symbolic_params['hk_val'], self.ground_params.hk) + self.symbol_to_value['hk'] = (self.symbolic_params['hk'], self.ground_params.hk) + + #//TODO: Fix the bogde on the index of the insolation + # Ground Temperature Parameters + if self.gotemperature_params is not None: + self.symbol_to_value['gnd_gamma'] = (self.symbolic_params['gnd_gamma'], self.gotemperature_params.gamma) + self.symbol_to_value['gnd_C_val'] = (self.symbolic_params['gnd_C_val'], self.gotemperature_params.C[0]) + self.symbol_to_value['gnd_C'] = (self.symbolic_params['gnd_C_val'], self.gotemperature_params.C[0]) + self.symbol_to_value['gnd_T0'] = (self.symbolic_params['gnd_T0'], self.gotemperature_params.T0) + + # Ocean Parameters + if self.oceanic_params is not None: + self.symbol_to_value['gp'] = (self.symbolic_params['gp'], self.oceanic_params.gp) + self.symbol_to_value['r'] = (self.symbolic_params['r'], self.oceanic_params.r) + self.symbol_to_value['h'] = (self.symbolic_params['h'], self.oceanic_params.h) + self.symbol_to_value['d'] = (self.symbolic_params['d'], self.oceanic_params.d) + + #//TODO: Fix the bogde on the index of the insolation + # Ocean Temperature Parameters + if self.gotemperature_params is not None: + self.symbol_to_value['ocn_gamma'] = (self.symbolic_params['ocn_gamma'], self.gotemperature_params.gamma) + self.symbol_to_value['ocn_C_val'] = (self.symbolic_params['ocn_C_val'], self.gotemperature_params.C[0]) + self.symbol_to_value['ocn_C'] = (self.symbolic_params['ocn_C_val'], self.gotemperature_params.C[0]) + self.symbol_to_value['ocn_T0'] = (self.symbolic_params['ocn_T0'], self.gotemperature_params.T0) + \ No newline at end of file diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index b8d78bd..c3c1800 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -7,11 +7,15 @@ """ from contextlib import redirect_stdout +from qgs.functions.tendencies import create_tendencies + import numpy as np import sparse as sp import sympy as sy import pickle +#//TODO: Check non stored IP version of this + class SymbolicTensorLinear(object): """Symbolic qgs tendencies tensor class. @@ -377,7 +381,7 @@ def _compute_tensor_dicts(self): jo = j + offset # skipping the theta 0 variable if it exists val_2 = a_theta_mult_a[i, j] * symbolic_params['kd'] * self.sig0 / 2 - val_3 = a_theta_mult_a[i, j] * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kpd']) * self.sig0 + val_3 = a_theta_mult_a[i, j] * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val_2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -val_3) @@ -763,7 +767,8 @@ def simplify_dict(dic): return dic_upp def save_to_file(self, filename, **kwargs): - """Function to save the tensor object to a file with the :mod:`pickle` module. + """F + unction to save the tensor object to a file with the :mod:`pickle` module. Parameters ---------- @@ -776,13 +781,71 @@ def save_to_file(self, filename, **kwargs): pickle.dump(self.__dict__, f, **kwargs) f.close() - def print_tensor(self): - nx, ny, nz = self.tensor.shape + def subs_tensor(self, tensor=None): + """ + Uses sympy substitution to convert the symbolic tensor to a numerical one. + """ + + self.params._set_symbolic_parameters() + + if tensor is not None: + symbolic_variables = tensor.free_symbols + else: + symbolic_variables = self.tensor.free_symbols + + key_list = list(self.sym_params.keys()) + item_list = list(self.sym_params.values()) + + symbol_to_number_map = list() + for sym in symbolic_variables: + key = key_list[item_list.index(sym)] + num = self.params.symbol_to_value[key] + symbol_to_number_map.append(num) + + + if tensor is not None: + ten_out = tensor.subs(symbol_to_number_map) + else: + ten_out = self.tensor.subs(symbol_to_number_map) + + return ten_out + + def test_tensor_numerically(self, tensor=None, tol=1e-10): + """ + Uses sympy substitution to convert the symbolic tensor to a numerical one. + This is then compared to the tensor calculated in the qgs.tensor.symbolic module. + + """ + + _, _, numerical_tensor = create_tendencies(self.params, return_qgtensor=True) + + subbed_ten = self.subs_tensor(tensor) + subbed_ten = np.array(subbed_ten) + + # Convert the substituted sympy array to a sparce one + subbed_tensor_np = np.array(subbed_ten).astype(np.float64) + subbed_tensor_sp = sp.COO(subbed_tensor_np) + + diff_arr = subbed_tensor_sp - numerical_tensor.tensor + self.print_tensor(diff_arr, tol) + + def print_tensor(self, tensor=None, tol=1e-10): + if tensor is not None: + temp_ten = tensor + else: + temp_ten = self.tensor + + nx, ny, nz = temp_ten.shape for i in range(nx): for j in range(ny): for k in range(nz): - if self.tensor[i, j, k] != 0: - print(str((i, j, k)) + ": " + str(self.tensor[i, j, k])) + if isinstance(temp_ten[i, j, k], float): + bool_test = abs(temp_ten[i, j, k]) > tol + else: + bool_test = temp_ten[i, j, k] + + if bool_test: + print(str((i, j, k)) + ": " + str(temp_ten[i, j, k])) class SymbolicTensorDynamicT(SymbolicTensorLinear): #//TODO: Need to work out symbolic tensor dot From a8903d08e9fe776d867d38d901be5b2989276d9c Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 25 Jul 2023 12:37:29 +0200 Subject: [PATCH 021/143] Fix of sign errors - Stored Linear Tensor checked for atm-ocn --- qgs/tensors/symbolic_qgtensor.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index c3c1800..3546793 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -160,7 +160,7 @@ def LR(self): @property def G(self): - return self.sym_params['L'] ** 2 / self.LR ** 2 + return -self.sym_params['L'] ** 2 / self.LR ** 2 @property def Cpgo(self): @@ -323,7 +323,7 @@ def _compute_tensor_dicts(self): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), a_inv_mult_c[i, j] * symbolic_params['beta']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -a_inv_mult_c[i, j] * symbolic_params['beta']) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -(symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) @@ -385,7 +385,7 @@ def _compute_tensor_dicts(self): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val_2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -val_3) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), a_theta_mult_c[i, j] * symbolic_params['beta'] * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -a_theta_mult_c[i, j] * symbolic_params['beta'] * self.sig0) if gp is not None: if symbolic_params['hk'] is not None: @@ -411,7 +411,7 @@ def _compute_tensor_dicts(self): if ocean: for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), a_theta_mult_d[i, j] * self.sig0 * symbolic_params['kd'] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), -a_theta_mult_d[i, j] * self.sig0 * symbolic_params['kd'] / 2) if self.Lpa is not None: for j in range(nvar[3]): @@ -438,7 +438,7 @@ def _compute_tensor_dicts(self): jo = j + offset # skipping the theta 0 variable if it exists sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), M_psio_mult_K[i, j] * symbolic_params['d']) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), M_psio_mult_K[i, j] * symbolic_params['d']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), -M_psio_mult_K[i, j] * symbolic_params['d']) for j in range(nvar[2]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_N[i, j] * symbolic_params['beta']) @@ -762,13 +762,13 @@ def simplify_dict(dic): for key in keys: new_key = tuple([key[0]] + sorted(key[1:])) + dic_upp = _add_to_dict(dic_upp, new_key, dic[key]) return dic_upp def save_to_file(self, filename, **kwargs): - """F - unction to save the tensor object to a file with the :mod:`pickle` module. + """Function to save the tensor object to a file with the :mod:`pickle` module. Parameters ---------- From 046cadcc54806e260a074b804bd65664e2817349 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 25 Jul 2023 13:03:11 +0200 Subject: [PATCH 022/143] Edit to functions to allow them to work with tensors of any rank --- qgs/tensors/symbolic_qgtensor.py | 43 ++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 3546793..3f8a5f9 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -720,8 +720,17 @@ def compute_tensor(self): def _set_tensor(self, dic): ndim = self.params.ndim - jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jacobian_from_dict(dic), (ndim + 1, ndim + 1, ndim + 1)) - tensor = sy.tensor.array.ImmutableSparseNDimArray(self.simplify_dict(dic), (ndim + 1, ndim + 1, ndim + 1)) + if self.params.dynamic_T: + if self.params.T4: + #//TODO: Make a proper error message for here + raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") + else: + dims = (ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1) + else: + dims = (ndim + 1, ndim + 1, ndim + 1) + + jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jacobian_from_dict(dic), dims) + tensor = sy.tensor.array.ImmutableSparseNDimArray(self.simplify_dict(dic), dims) self.jacobian_tensor = jacobian_tensor.applyfunc(sy.simplify) self.tensor = tensor.applyfunc(sy.simplify) @@ -827,7 +836,7 @@ def test_tensor_numerically(self, tensor=None, tol=1e-10): subbed_tensor_sp = sp.COO(subbed_tensor_np) diff_arr = subbed_tensor_sp - numerical_tensor.tensor - self.print_tensor(diff_arr, tol) + self.print_tensor(diff_arr.todense(), tol) def print_tensor(self, tensor=None, tol=1e-10): if tensor is not None: @@ -835,17 +844,16 @@ def print_tensor(self, tensor=None, tol=1e-10): else: temp_ten = self.tensor - nx, ny, nz = temp_ten.shape - for i in range(nx): - for j in range(ny): - for k in range(nz): - if isinstance(temp_ten[i, j, k], float): - bool_test = abs(temp_ten[i, j, k]) > tol - else: - bool_test = temp_ten[i, j, k] + val_list = np.ndenumerate(temp_ten) - if bool_test: - print(str((i, j, k)) + ": " + str(temp_ten[i, j, k])) + for ix, v in val_list: + if isinstance(v, float): + bool_test = (abs(v) > tol) + else: + bool_test = (v != 0) + + if bool_test: + print(str(ix) + ": " + str(v)) class SymbolicTensorDynamicT(SymbolicTensorLinear): #//TODO: Need to work out symbolic tensor dot @@ -1169,8 +1177,10 @@ def _compute_non_stored_full_dict(self): def compute_tensor(self): """Routine to compute the tensor.""" # gathering - par = self.params - + if self.params.T4: + #//TODO: Make a proper error message for here + raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") + symbolic_dict_linear = SymbolicTensorLinear.compute_tensor(self) symbolic_dict_linear = _shift_dict_keys(symbolic_dict_linear, (0, 0)) @@ -1179,7 +1189,8 @@ def compute_tensor(self): if symbolic_dict_linear is not None: symbolic_dict_dynT = {**symbolic_dict_linear, **symbolic_dict_dynT} - return symbolic_dict_dynT + if symbolic_dict_dynT is not None: + self._set_tensor(symbolic_dict_dynT) def _kronecker_delta(i, j): From 5b5a39f2698827cf7073e4c58f47ce945012e251 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 25 Jul 2023 13:49:01 +0200 Subject: [PATCH 023/143] Bugs in DE tensor calc fixes --- qgs/params/params.py | 39 ++++++++++++++------------------ qgs/tensors/symbolic_qgtensor.py | 4 ++-- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/qgs/params/params.py b/qgs/params/params.py index 7ea42e7..5c364b6 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -881,10 +881,12 @@ class QgParams(Params): # Ground Temperature Parameters 'gnd_gamma': sy.Symbol('gamma_g'), - 'gnd_C_val': sy.Symbol('C_g'), - 'gnd_C': None, 'gnd_T0': sy.Symbol('T_g0'), - + + # Ground/ocean Parameters + 'go_C_val': sy.Symbol('C_go'), + 'go_C': None, + # Ocean Parameters 'gp': sy.Symbol('g_p'), 'r': sy.Symbol('r'), @@ -893,8 +895,6 @@ class QgParams(Params): # Ocean Temperature Parameters 'ocn_gamma': sy.Symbol('gamma_o'), - 'ocn_C_val': sy.Symbol('C_o'), - 'ocn_C': None, 'ocn_T0': sy.Symbol('T_o0') } @@ -1270,26 +1270,21 @@ def symbolic_insolation_array(self, Cpa=None, Cpgo=None): if Cpa is not None: atm_C_list = Cpa elif Cpgo is not None: - gnd_C_list = Cpgo - ocn_C_list = Cpgo - else: - atm_C_list = [0] * self.nmod[0] - gnd_C_list = [0] * self.nmod[0] - ocn_C_list = [0] * self.nmod[0] + go_C_list = Cpgo + else: + atm_C_list = [0] * self.number_of_variables[1] + go_C_list = [0] * self.number_of_variables[1] atm_C_list[0] = self.symbolic_params['atm_C_val'] - gnd_C_list[0] = self.symbolic_params['gnd_C_val'] - ocn_C_list[0] = self.symbolic_params['ocn_C_val'] + go_C_list[0] = self.symbolic_params['go_C_val'] if self.dynamic_T: atm_C_list[1] = self.symbolic_params['atm_C_val'] - gnd_C_list[1] = self.symbolic_params['gnd_C_val'] - ocn_C_list[1] = self.symbolic_params['ocn_C_val'] + go_C_list[1] = self.symbolic_params['go_C_val'] self.symbolic_params['atm_C'] = sy.matrices.immutable.ImmutableSparseMatrix(atm_C_list) - self.symbolic_params['gnd_C'] = sy.matrices.immutable.ImmutableSparseMatrix(gnd_C_list) - self.symbolic_params['ocn_C'] = sy.matrices.immutable.ImmutableSparseMatrix(ocn_C_list) + self.symbolic_params['go_C'] = sy.matrices.immutable.ImmutableSparseMatrix(go_C_list) def symbolic_orography_array(self, hk=None): """Set the array hk from given value, or the defulat symbols @@ -2073,7 +2068,6 @@ def _set_ground_analytic_fourier_modes(self, nxmax=None, nymax=None, auto=True): for i in range(self.nmod[1]): self._ground_latex_var_string.append(r'delta T_{\rm g,' + str(i + 1) + "}") self._ground_var_string.append(r'delta_T_g_' + str(i + 1)) - def _set_symbolic_parameters(self): """ @@ -2120,10 +2114,13 @@ def _set_symbolic_parameters(self): # Ground Temperature Parameters if self.gotemperature_params is not None: self.symbol_to_value['gnd_gamma'] = (self.symbolic_params['gnd_gamma'], self.gotemperature_params.gamma) - self.symbol_to_value['gnd_C_val'] = (self.symbolic_params['gnd_C_val'], self.gotemperature_params.C[0]) - self.symbol_to_value['gnd_C'] = (self.symbolic_params['gnd_C_val'], self.gotemperature_params.C[0]) self.symbol_to_value['gnd_T0'] = (self.symbolic_params['gnd_T0'], self.gotemperature_params.T0) + # Ground/ocean Parameters + if self.gotemperature_params is not None: + self.symbol_to_value['go_C_val'] = (self.symbolic_params['go_C_val'], self.gotemperature_params.C[0]) + self.symbol_to_value['go_C'] = (self.symbolic_params['go_C_val'], self.gotemperature_params.C[0]) + # Ocean Parameters if self.oceanic_params is not None: self.symbol_to_value['gp'] = (self.symbolic_params['gp'], self.oceanic_params.gp) @@ -2135,7 +2132,5 @@ def _set_symbolic_parameters(self): # Ocean Temperature Parameters if self.gotemperature_params is not None: self.symbol_to_value['ocn_gamma'] = (self.symbolic_params['ocn_gamma'], self.gotemperature_params.gamma) - self.symbol_to_value['ocn_C_val'] = (self.symbolic_params['ocn_C_val'], self.gotemperature_params.C[0]) - self.symbol_to_value['ocn_C'] = (self.symbolic_params['ocn_C_val'], self.gotemperature_params.C[0]) self.symbol_to_value['ocn_T0'] = (self.symbolic_params['ocn_T0'], self.gotemperature_params.T0) \ No newline at end of file diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 3f8a5f9..28968a6 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -164,7 +164,7 @@ def G(self): @property def Cpgo(self): - return self.sym_params['gnd_C'] / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) * self.params.rr / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) + return self.sym_params['go_C'] / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) * self.params.rr / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) @property def Lpgo(self): @@ -452,7 +452,7 @@ def _compute_tensor_dicts(self): U_inv_mult_W = _symbolic_tensordot(U_inv, bips._W, axes=1) U_inv_mult_W_Cpgo = _symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) - U_inv_mult_O = _symbolic_tensordot(U_inv, bips._O[offset:, offset:, offset:], axes=1) + U_inv_mult_O = _symbolic_tensordot(U_inv, bips._O[:, offset:, offset:], axes=1) for i in range(nvar[3]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i][0]) From 20da1967d46a493e733c88db1467f0ddca3cf8e7 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 25 Jul 2023 16:23:12 +0200 Subject: [PATCH 024/143] Update to dynamic_T loop for speed --- qgs/tensors/symbolic_qgtensor.py | 99 ++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 28968a6..9673b7f 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -215,7 +215,6 @@ def T4LSBpa(self): #//TODO: Do i need the scaling parameters? def _compute_tensor_dicts(self): - if self.params is None: return None @@ -342,7 +341,6 @@ def _compute_tensor_dicts(self): if ocean: for j in range(nvar[2]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * symbolic_params['kd'] / 2) - # theta_a part a_theta_mult_u = _symbolic_tensordot(a_theta, aips._u, axes=1) if self.Cpa is not None: @@ -709,13 +707,11 @@ def _compute_tensor_dicts(self): def compute_tensor(self): """Routine to compute the tensor.""" - sy_arr_dic = self._compute_tensor_dicts() sy_arr_dic = self.remove_dic_zeros(sy_arr_dic) if sy_arr_dic is not None: - if not(self.params.dynamic_T): - self._set_tensor(sy_arr_dic) + self._set_tensor(sy_arr_dic) def _set_tensor(self, dic): ndim = self.params.ndim @@ -731,6 +727,7 @@ def _set_tensor(self, dic): jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jacobian_from_dict(dic), dims) tensor = sy.tensor.array.ImmutableSparseNDimArray(self.simplify_dict(dic), dims) + self.jacobian_tensor = jacobian_tensor.applyfunc(sy.simplify) self.tensor = tensor.applyfunc(sy.simplify) @@ -921,7 +918,7 @@ def _compute_tensor_dicts(self): go = True if aips.stored and go: - symbolic_array_full_dict = self._compute_stored_full_dict() + symbolic_array_full_dict = self._compute_stored_full_dict_2() else: symbolic_array_full_dict = self._compute_non_stored_full_dict() @@ -975,58 +972,88 @@ def _compute_stored_full_dict(self): sy_arr_dic = dict() # theta_a part - a_theta_mult_z = _symbolic_tensordot(a_theta, aips._z, axes=1) - a_theta_mult_v = _symbolic_tensordot(a_theta, aips._v, axes=1) - + val = 0 for i in range(nvar[1]): - if self.T4LSBpa is not None: j = k = ell = 0 for m in range(nvar[1]): - val = self.T4LSBpa * a_theta_mult_z[i, j, k, ell, m] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), val) - + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips._z[jj, j, k, ell, m] + if m == 0: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4LSBpa * val) + else: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4LSBpa * val) + if ocean: if self.T4LSBpgo is not None: j = k = ell = 0 for m in range(nvar[3]): - val = self.T4LSBpgo * a_theta_mult_v[i, j, k, ell, m] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), val) + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips._v[jj, j, k, ell, m] + if m == 0: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), self.T4LSBpgo * val) + else: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), 4 * self.T4LSBpgo * val) if ground_temp: if self.T4LSBpgo is not None: j = k = ell = 0 for m in range(nvar[2]): - val = self.T4LSBpgo * a_theta_mult_v[i, j, k, ell, m] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), val) + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips._v[jj, j, k, ell, m] + if m == 0: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), self.T4LSBpgo * val) + else: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), 4 * self.T4LSBpgo * val) if ocean: - # deltaT_o part - U_inv_mult_Z = _symbolic_tensordot(U_inv, bips._Z, axes=1) - U_inv_mult_V = _symbolic_tensordot(U_inv, bips._V, axes=1) - + #delta_T part for i in range(nvar[3]): j = k = ell = 0 for m in range(nvar[1]): - val = self.T4sbpa * U_inv_mult_Z[i, j, k, ell, m] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), val) - - val = - self.T4sbpgo * U_inv_mult_V[i, j, k, ell, m] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), val) + val = 0 + for jj in range(nvar[3]): + val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] + if m == 0: + # val = self.T4sbpa * U_inv_mult_Z[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) + else: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) + + for m in range(nvar[3]): + val = 0 + for jj in range(nvar[3]): + val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] + if m == 0: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpgo * val) + else: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpgo * val) if ground_temp: # deltaT_g part - U_inv_mult_Z = _symbolic_tensordot(U_inv, bips._Z, axes=1) - U_inv_mult_V = _symbolic_tensordot(U_inv, bips._V, axes=1) - for i in range(nvar[2]): j = k = ell = 0 for m in range(nvar[1]): - val = self.T4sbpa * U_inv_mult_Z[i, j, k, ell, m] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), val) + val = 0 + for jj in range(nvar[2]): + val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] + if m == 0: + # val = self.T4sbpa * U_inv_mult_Z[i, j, k, ell, m] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) + else: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) - val = - self.T4sbpgo * U_inv_mult_V[i, j, k, ell, m] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), val) + for m in range(nvar[2]): + val = 0 + for jj in range(nvar[2]): + val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] + if m == 0: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), self.T4sbpgo * val) + else: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), 4 * self.T4sbpgo * val) return sy_arr_dic @@ -1180,11 +1207,11 @@ def compute_tensor(self): if self.params.T4: #//TODO: Make a proper error message for here raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") - - symbolic_dict_linear = SymbolicTensorLinear.compute_tensor(self) + + symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) symbolic_dict_linear = _shift_dict_keys(symbolic_dict_linear, (0, 0)) - symbolic_dict_dynT = self._compute_tensor_dicts(self) + symbolic_dict_dynT = self._compute_tensor_dicts() if symbolic_dict_linear is not None: symbolic_dict_dynT = {**symbolic_dict_linear, **symbolic_dict_dynT} From 7005af06186da3c15d2a556efd02e4bb7fc99663 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 25 Jul 2023 17:15:22 +0200 Subject: [PATCH 025/143] cleaning up the file --- qgs/tensors/symbolic_qgtensor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 9673b7f..831566c 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -825,13 +825,16 @@ def test_tensor_numerically(self, tensor=None, tol=1e-10): _, _, numerical_tensor = create_tendencies(self.params, return_qgtensor=True) + print("subs") subbed_ten = self.subs_tensor(tensor) subbed_ten = np.array(subbed_ten) + print("numpy and spipy") # Convert the substituted sympy array to a sparce one subbed_tensor_np = np.array(subbed_ten).astype(np.float64) subbed_tensor_sp = sp.COO(subbed_tensor_np) + print("comparison") diff_arr = subbed_tensor_sp - numerical_tensor.tensor self.print_tensor(diff_arr.todense(), tol) @@ -918,7 +921,7 @@ def _compute_tensor_dicts(self): go = True if aips.stored and go: - symbolic_array_full_dict = self._compute_stored_full_dict_2() + symbolic_array_full_dict = self._compute_stored_full_dict() else: symbolic_array_full_dict = self._compute_non_stored_full_dict() From 222fc0a6768629ccba51d6fd97198caee4e2f556 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 25 Jul 2023 17:30:31 +0200 Subject: [PATCH 026/143] notebooks used to test tensor --- ...put.ipynb => Symbolic Output-Linear.ipynb} | 198 +++++-------- notebooks/Symbolic Output-dynamic_T.ipynb | 263 ++++++++++++++++++ 2 files changed, 329 insertions(+), 132 deletions(-) rename notebooks/{Symbolic Output.ipynb => Symbolic Output-Linear.ipynb} (63%) create mode 100644 notebooks/Symbolic Output-dynamic_T.ipynb diff --git a/notebooks/Symbolic Output.ipynb b/notebooks/Symbolic Output-Linear.ipynb similarity index 63% rename from notebooks/Symbolic Output.ipynb rename to notebooks/Symbolic Output-Linear.ipynb index d052069..31c4f52 100644 --- a/notebooks/Symbolic Output.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -11,6 +11,19 @@ "sys.path.extend([os.path.abspath('../')])" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b3814d5", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import sympy as sy\n", + "import sparse as sp\n", + "import math" + ] + }, { "cell_type": "code", "execution_count": null, @@ -18,7 +31,8 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.params.params import QgParams" + "from qgs.params.params import QgParams\n", + "from qgs.functions.tendencies import create_tendencies" ] }, { @@ -28,7 +42,7 @@ "metadata": {}, "outputs": [], "source": [ - "model_parameters = QgParams(dynamic_T=True)" + "model_parameters = QgParams(dynamic_T=False)" ] }, { @@ -54,16 +68,6 @@ "model_parameters.symbolic_params" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "36811d45", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.symbolic_params['atm_gamma']" - ] - }, { "cell_type": "code", "execution_count": null, @@ -73,7 +77,8 @@ "source": [ "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear" + "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", + "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" ] }, { @@ -94,260 +99,189 @@ { "cell_type": "code", "execution_count": null, - "id": "6dcb5b8c", - "metadata": {}, - "outputs": [], - "source": [ - "ocn_ip.return_symbolic" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f4c7717f", - "metadata": {}, + "id": "35d77653", + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "import sympy as sy" + "%%time\n", + "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", + "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "c5064c91", + "id": "70086b90", "metadata": {}, "outputs": [], "source": [ - "# U_inv = dict()\n", - "\n", - "# for i in range(model_parameters.number_of_variables[3]):\n", - "# for j in range(model_parameters.number_of_variables[3]):\n", - "# U_inv[(i, j)] = ocn_ip.U(i, j)\n", - "# U_inv = sy.matrices.immutable.ImmutableSparseMatrix(model_parameters.number_of_variables[3], model_parameters.number_of_variables[3], U_inv)\n", - "# U_inv = sy.matrices.Inverse(U_inv)" + "# atm_ip_stored.save_to_file('temp_atm_sym_lin.ip')\n", + "# ocn_ip_stored.save_to_file('temp_ocn_sym_lin.ip')" ] }, { "cell_type": "code", "execution_count": null, - "id": "e28d4eee", - "metadata": {}, + "id": "ea85360f", + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "# U_inv" + "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)\n", + "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "a2d3c783", + "id": "196d5440", "metadata": {}, "outputs": [], "source": [ - "# U_inv = dict()\n", - "\n", - "# for i in range(model_parameters.number_of_variables[3]):\n", - "# for j in range(model_parameters.number_of_variables[3]):\n", - "# U_inv[(i, j)] = ocn_ip_stored.U(i, j)\n", - "# U_inv = sy.matrices.immutable.ImmutableSparseMatrix(model_parameters.number_of_variables[3], model_parameters.number_of_variables[3], U_inv)\n", - "# U_inv = sy.matrices.Inverse(U_inv)" + "atm_loaded.load_from_file('temp_atm_sym_lin.ip')\n", + "ocn_loaded.load_from_file('temp_ocn_sym_lin.ip')" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "2512cef4", + "cell_type": "markdown", + "id": "d43d341b", "metadata": {}, - "outputs": [], "source": [ - "# U_inv" + "## Testing the symbolic_qgtensor" ] }, { "cell_type": "code", "execution_count": null, - "id": "041f2236", + "id": "d964e8f2", "metadata": {}, "outputs": [], "source": [ - "# ocn_ip_stored._Z.shape" + "model_parameters.symbolic_params['go_C']" ] }, { "cell_type": "code", "execution_count": null, - "id": "768c0226", + "id": "e6cb2cda", "metadata": {}, "outputs": [], "source": [ - "# from sympy import tensorproduct" + "model_parameters.number_of_variables" ] }, { "cell_type": "code", "execution_count": null, - "id": "e006ef35", + "id": "19d0dd71", "metadata": {}, "outputs": [], "source": [ - "# x = U_inv[1, :] * ocn_ip_stored._Z[:, :, :, :, :]" + "ocn_loaded._O.shape" ] }, { "cell_type": "code", "execution_count": null, - "id": "78bb6c3a", + "id": "2e17b891", "metadata": {}, "outputs": [], "source": [ - "# x.shape" + "ten = SymbolicTensorLinear(params=model_parameters, atmospheric_inner_products=atm_loaded, oceanic_inner_products=ocn_loaded)" ] }, { "cell_type": "code", "execution_count": null, - "id": "76f0cb66", + "id": "e2037a75", "metadata": {}, "outputs": [], "source": [ - "# model_parameters.symbolic_params['atm_T0']" + "ten.params.dynamic_T" ] }, { "cell_type": "code", "execution_count": null, - "id": "35d77653", + "id": "cf668cb9", "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ "%%time\n", - "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", - "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" + "ten.compute_tensor()" ] }, { "cell_type": "code", "execution_count": null, - "id": "70086b90", + "id": "425df2fd", "metadata": {}, "outputs": [], "source": [ - "# atm_ip_stored.save_to_file('temp_atm_sym.ip')\n", - "# ocn_ip_stored.save_to_file('temp_ocn_sym.ip')" + "ten.tensor.shape" ] }, { "cell_type": "code", "execution_count": null, - "id": "ea85360f", + "id": "8ed132e4", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters)\n", - "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters)" + "subs_list = ten.test_tensor_numerically(tol=1e-12)" ] }, { "cell_type": "code", "execution_count": null, - "id": "196d5440", - "metadata": {}, - "outputs": [], - "source": [ - "atm_loaded.load_from_file('temp_atm_sym.ip')\n", - "ocn_loaded.load_from_file('temp_ocn_sym.ip')" - ] - }, - { - "cell_type": "markdown", - "id": "d43d341b", - "metadata": {}, - "source": [ - "## Testing the symbolic_qgtensor" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2e17b891", - "metadata": {}, - "outputs": [], - "source": [ - "ten = SymbolicTensorLinear(params=model_parameters, atmospheric_inner_products=atm_loaded, oceanic_inner_products=ocn_loaded)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cf668cb9", - "metadata": {}, - "outputs": [], - "source": [ - "ten.compute_tensor()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "91d6fde6", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f7d9a482", + "id": "3aa7adca", "metadata": {}, "outputs": [], "source": [ - "a_inv = dict()\n", - "for i in range(0, 10):\n", - " for j in range(0, 10):\n", - " a_inv[(i - 0, j - 0)] = atm_loaded.a(i, j)\n", - "\n", - "a_inv = sy.matrices.immutable.ImmutableSparseMatrix(10, 10, a_inv)\n", - "a_inv = sy.matrices.Inverse(a_inv)" + "ten.print_tensor(np.array(ten.subs_tensor()).astype(np.float64))" ] }, { "cell_type": "code", "execution_count": null, - "id": "06362de1", + "id": "bd34e996", "metadata": {}, "outputs": [], "source": [ - "a_inv" + "ten.print_tensor(num_ten.tensor.todense())" ] }, { "cell_type": "code", "execution_count": null, - "id": "88a32f70", + "id": "3526eac0", "metadata": {}, "outputs": [], "source": [ - "atm_loaded._c[1:, 2].shape" + "ten.tensor[2, 0, 3]" ] }, { "cell_type": "code", "execution_count": null, - "id": "b6c23fca", + "id": "1817f522", "metadata": {}, "outputs": [], "source": [ - "a_inv[1, :] * atm_loaded._c[1:, 2]" + "list(np.ndenumerate(ten.tensor))" ] }, { "cell_type": "code", "execution_count": null, - "id": "c23f807c", + "id": "45e42d95", "metadata": {}, "outputs": [], "source": [] diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb new file mode 100644 index 0000000..88f8192 --- /dev/null +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -0,0 +1,263 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "12f59a3f", + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "sys.path.extend([os.path.abspath('../')])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b3814d5", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import sympy as sy\n", + "import sparse as sp\n", + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7711e102", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.params.params import QgParams\n", + "from qgs.functions.tendencies import create_tendencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf632fac", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters = QgParams(dynamic_T=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2a8e7c1", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", + "# Mode truncation at the wavenumber 2 in the x and at the \n", + "# wavenumber 4 in the y spatial coordinates for the ocean\n", + "model_parameters.set_oceanic_basin_fourier_modes(2, 4, mode=\"symbolic\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "006f3c7d", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.symbolic_params" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8500ea89", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", + "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", + "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", + "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6112c83a", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "# atm_ip = AtmosphericSymbolicInnerProducts(model_parameters)\n", + "# ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", + "ocn_ip = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35d77653", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", + "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70086b90", + "metadata": {}, + "outputs": [], + "source": [ + "# atm_ip_stored.save_to_file('temp_atm_sym_de.ip')\n", + "# ocn_ip_stored.save_to_file('temp_ocn_sym_de.ip')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea85360f", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)\n", + "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "196d5440", + "metadata": {}, + "outputs": [], + "source": [ + "atm_loaded.load_from_file('temp_atm_sym_de.ip')\n", + "ocn_loaded.load_from_file('temp_ocn_sym_de.ip')" + ] + }, + { + "cell_type": "markdown", + "id": "d43d341b", + "metadata": {}, + "source": [ + "## Testing the symbolic_qgtensor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e17b891", + "metadata": {}, + "outputs": [], + "source": [ + "ten = SymbolicTensorDynamicT(params=model_parameters, atmospheric_inner_products=atm_loaded, oceanic_inner_products=ocn_loaded)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cf668cb9", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "%%time\n", + "ten.compute_tensor()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "775c3dcd", + "metadata": {}, + "outputs": [], + "source": [ + "ten.tensor.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7f40da39", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "_, _, num_ten = create_tendencies(model_parameters, return_qgtensor=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ed132e4", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "subs_list = ten.test_tensor_numerically(tol=1e-12)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3aa7adca", + "metadata": {}, + "outputs": [], + "source": [ + "ten.print_tensor(np.array(ten.subs_tensor()).astype(np.float64))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bd34e996", + "metadata": {}, + "outputs": [], + "source": [ + "ten.print_tensor(num_ten.tensor.todense())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45e42d95", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 5012c93323302984fe1d1c7ed369560d295a94ea Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 26 Jul 2023 12:13:42 +0200 Subject: [PATCH 027/143] Update to do all calculations using dictionaries not symbolic tensors --- qgs/tensors/symbolic_qgtensor.py | 117 +++++++++++++++++++------------ 1 file changed, 71 insertions(+), 46 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 831566c..22501e5 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -705,7 +705,7 @@ def _compute_tensor_dicts(self): return sy_arr_dic - def compute_tensor(self): + def compute_tensor(self, ): """Routine to compute the tensor.""" sy_arr_dic = self._compute_tensor_dicts() sy_arr_dic = self.remove_dic_zeros(sy_arr_dic) @@ -713,7 +713,14 @@ def compute_tensor(self): if sy_arr_dic is not None: self._set_tensor(sy_arr_dic) - def _set_tensor(self, dic): + def _set_tensor(self, dic, set_symbolic=False): + self.jac_dic = self.remove_dic_zeros(self.jacobian_from_dict(dic)) + self.tensor_dic = self.remove_dic_zeros(self.simplify_dict(dic)) + + if set_symbolic: + self._set_symbolic_tensor() + + def _set_symbolic_tensor(self): ndim = self.params.ndim if self.params.dynamic_T: @@ -725,8 +732,9 @@ def _set_tensor(self, dic): else: dims = (ndim + 1, ndim + 1, ndim + 1) - jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jacobian_from_dict(dic), dims) - tensor = sy.tensor.array.ImmutableSparseNDimArray(self.simplify_dict(dic), dims) + #//TODO: I needed to make copies here, but I am not sure why. If I tried to input the dict without the copy it messed up the keys + jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jac_dic.copy(), dims) + tensor = sy.tensor.array.ImmutableSparseNDimArray(self.tensor_dic.copy(), dims) self.jacobian_tensor = jacobian_tensor.applyfunc(sy.simplify) self.tensor = tensor.applyfunc(sy.simplify) @@ -768,7 +776,6 @@ def simplify_dict(dic): for key in keys: new_key = tuple([key[0]] + sorted(key[1:])) - dic_upp = _add_to_dict(dic_upp, new_key, dic[key]) return dic_upp @@ -787,64 +794,89 @@ def save_to_file(self, filename, **kwargs): pickle.dump(self.__dict__, f, **kwargs) f.close() - def subs_tensor(self, tensor=None): + def subs_tensor(self, tensor=None, dict_opp=True): """ - Uses sympy substitution to convert the symbolic tensor to a numerical one. + Uses sympy substitution to convert the symbolic tensor or a symbolic dictionary to a numerical one. """ self.params._set_symbolic_parameters() - if tensor is not None: - symbolic_variables = tensor.free_symbols - else: - symbolic_variables = self.tensor.free_symbols - - key_list = list(self.sym_params.keys()) - item_list = list(self.sym_params.values()) - symbol_to_number_map = list() - for sym in symbolic_variables: - key = key_list[item_list.index(sym)] - num = self.params.symbol_to_value[key] - symbol_to_number_map.append(num) + for key in self.sym_params.keys(): + try: + symbol_to_number_map.append(self.params.symbol_to_value[key]) + except: + pass + + if isinstance(tensor, dict): + ten_out = dict() + for key in tensor.keys(): + ten_out[key] = tensor[key].subs(symbol_to_number_map) + + elif dict_opp: + ten_out = dict() + for key in self.tensor_dic.keys(): + ten_out[key] = self.tensor_dic[key].subs(symbol_to_number_map) - if tensor is not None: - ten_out = tensor.subs(symbol_to_number_map) else: - ten_out = self.tensor.subs(symbol_to_number_map) + if tensor is not None: + ten_out = tensor.subs(symbol_to_number_map) + else: + ten_out = self.tensor.subs(symbol_to_number_map) return ten_out - def test_tensor_numerically(self, tensor=None, tol=1e-10): + def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): """ - Uses sympy substitution to convert the symbolic tensor to a numerical one. + Uses sympy substitution to convert the symbolic tensor, or symbolic dictionary, to a numerical one. This is then compared to the tensor calculated in the qgs.tensor.symbolic module. """ + ndim = self.params.ndim + + if self.params.dynamic_T: + if self.params.T4: + #//TODO: Make a proper error message for here + raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") + else: + dims = (ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1) + else: + dims = (ndim + 1, ndim + 1, ndim + 1) _, _, numerical_tensor = create_tendencies(self.params, return_qgtensor=True) - print("subs") - subbed_ten = self.subs_tensor(tensor) - subbed_ten = np.array(subbed_ten) + if tensor is None: + if dict_opp: + tensor = self.tensor_dic + else: + tensor = self.tensor - print("numpy and spipy") - # Convert the substituted sympy array to a sparce one - subbed_tensor_np = np.array(subbed_ten).astype(np.float64) - subbed_tensor_sp = sp.COO(subbed_tensor_np) + subbed_ten = self.subs_tensor(tensor) + #//TODO: Clean up this messy COO + if isinstance(subbed_ten, dict): + subbed_tensor_sp = sp.COO(np.array([list(k) for k in subbed_ten.keys()]).T, np.array(list(subbed_ten.values()), dtype=float), shape=dims) + else: + subbed_ten = np.array(subbed_ten) + subbed_tensor_np = np.array(subbed_ten).astype(np.float64) + subbed_tensor_sp = sp.COO.from_numpy(subbed_tensor_np) - print("comparison") - diff_arr = subbed_tensor_sp - numerical_tensor.tensor - self.print_tensor(diff_arr.todense(), tol) + diff_arr = subbed_tensor_sp.todense() - numerical_tensor.tensor.todense() + self.print_tensor(diff_arr, tol) - def print_tensor(self, tensor=None, tol=1e-10): - if tensor is not None: - temp_ten = tensor + def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): + if tensor is None: + if dict_opp: + temp_ten = self.tensor_dic + else: + temp_ten = self.tensor else: - temp_ten = self.tensor + temp_ten = tensor - val_list = np.ndenumerate(temp_ten) + if isinstance(temp_ten, dict): + val_list = [(key, temp_ten[key]) for key in temp_ten.keys()] + else: + val_list = np.ndenumerate(temp_ten) for ix, v in val_list: if isinstance(v, float): @@ -1021,7 +1053,6 @@ def _compute_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - # val = self.T4sbpa * U_inv_mult_Z[i, j, k, ell, m] sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) else: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) @@ -1044,7 +1075,6 @@ def _compute_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - # val = self.T4sbpa * U_inv_mult_Z[i, j, k, ell, m] sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) else: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) @@ -1114,7 +1144,6 @@ def _compute_non_stored_full_dict(self): sy_arr_dic = dict() # theta_a part for i in range(nvar[1]): - # t_full = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='dok') if self.T4LSBpa is not None: j = k = ell = 0 @@ -1156,8 +1185,6 @@ def _compute_non_stored_full_dict(self): # deltaT_o part for i in range(nvar[3]): - # t_full = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='dok') - j = k = ell = 0 for m in range(nvar[1]): val = 0 @@ -1181,8 +1208,6 @@ def _compute_non_stored_full_dict(self): if ground_temp: for i in range(nvar[2]): - # t_full = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='dok') - j = k = ell = 0 for m in range(nvar[1]): val = 0 From ce956d0f54649704ab3efb8ac5b772be3b8a2e08 Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 26 Jul 2023 18:21:17 +0200 Subject: [PATCH 028/143] issue with DE qg tensor - not fixed --- qgs/inner_products/symbolic.py | 48 +++++++++++++++++++------ qgs/params/params.py | 7 ++-- qgs/tensors/symbolic_qgtensor.py | 60 ++++++++++++++++++++++---------- 3 files changed, 81 insertions(+), 34 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index e2a79e3..0548098 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -884,6 +884,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) elif self._dynamic_T: # Z inner products + print("Running Z") args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.noc) for m in range(natm)] @@ -983,6 +984,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) elif self._dynamic_T: # V inner products + print("Running V") args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.noc) for m in range(self.noc)] @@ -1352,6 +1354,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): for j in range(self.ngr) for k in range(j, self.ngr) for ell in range(k, self.ngr) for m in range(ell, self.ngr)] output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) + elif self._dynamic_T: # V inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] @@ -1567,7 +1570,7 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False break def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False): - result_list = list() + result_list = dict() #//TODO: I am not sure what this timeout does, I have switched if off as it was leading to non-convergence. future = pool.map(_apply, args_list, timeout=None) @@ -1578,9 +1581,22 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False try: res = next(results) if subs is not None: - result_list.append(res[1].subs(subs)) + result_list[res[0]] = res[1].subs(subs) else: - result_list.append(res[1]) + result_list[res[0]] = res[1] + + if permute: + while True: + try: + i = res[0][0] + print(res[0]) + idx = res[0][1:] + perm_idx = multiset_permutations(idx) + for perm in perm_idx: + idx = [i] + perm + result_list[tuple(idx)] = res[1] + except StopIteration: + break except StopIteration: break @@ -1591,13 +1607,25 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False future = pool.map(_num_apply, num_args_list) results = future.result() - while True: - try: - res = next(results) - result_list.append(res[1]) - - except StopIteration: - break + if permute: + while True: + try: + res = next(results) + i = res[0][0] + idx = res[0][1:] + perm_idx = multiset_permutations(idx) + for perm in perm_idx: + idx = [i] + perm + result_list[tuple(idx)] = res[1] + except StopIteration: + break + else: + while True: + try: + res = next(results) + result_list[res[0]] = res[1] + except StopIteration: + break return result_list diff --git a/qgs/params/params.py b/qgs/params/params.py index 5c364b6..cd4dfca 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -881,11 +881,11 @@ class QgParams(Params): # Ground Temperature Parameters 'gnd_gamma': sy.Symbol('gamma_g'), - 'gnd_T0': sy.Symbol('T_g0'), # Ground/ocean Parameters 'go_C_val': sy.Symbol('C_go'), 'go_C': None, + 'go_T0': sy.Symbol('T_go0'), # Ocean Parameters 'gp': sy.Symbol('g_p'), @@ -895,7 +895,6 @@ class QgParams(Params): # Ocean Temperature Parameters 'ocn_gamma': sy.Symbol('gamma_o'), - 'ocn_T0': sy.Symbol('T_o0') } @@ -2114,12 +2113,12 @@ def _set_symbolic_parameters(self): # Ground Temperature Parameters if self.gotemperature_params is not None: self.symbol_to_value['gnd_gamma'] = (self.symbolic_params['gnd_gamma'], self.gotemperature_params.gamma) - self.symbol_to_value['gnd_T0'] = (self.symbolic_params['gnd_T0'], self.gotemperature_params.T0) # Ground/ocean Parameters if self.gotemperature_params is not None: self.symbol_to_value['go_C_val'] = (self.symbolic_params['go_C_val'], self.gotemperature_params.C[0]) self.symbol_to_value['go_C'] = (self.symbolic_params['go_C_val'], self.gotemperature_params.C[0]) + self.symbol_to_value['go_T0'] = (self.symbolic_params['go_T0'], self.gotemperature_params.T0) # Ocean Parameters if self.oceanic_params is not None: @@ -2132,5 +2131,3 @@ def _set_symbolic_parameters(self): # Ocean Temperature Parameters if self.gotemperature_params is not None: self.symbol_to_value['ocn_gamma'] = (self.symbolic_params['ocn_gamma'], self.gotemperature_params.gamma) - self.symbol_to_value['ocn_T0'] = (self.symbolic_params['ocn_T0'], self.gotemperature_params.T0) - \ No newline at end of file diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 22501e5..7daca31 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -182,19 +182,31 @@ def Lpa(self): @property def sbpgo(self): - return 4 * self.params.sb * self.sym_params['gnd_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + if self.params.gotemperature_params.T0 is None: + None + else: + return 4 * self.params.sb * self.sym_params['go_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) @property def sbpa(self): - return 8 * self.sym_params['eps'] * self.params.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + if self.params.atemperature_params.T0 is None: + return None + else: + return 8 * self.sym_params['eps'] * self.params.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) @property def LSBpgo(self): - return 2 * self.sym_params['eps'] * self.params.sb * self.sym_params['gnd_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + if self.params.gotemperature_params.T0 is None: + None + else: + return 2 * self.sym_params['eps'] * self.params.sb * self.sym_params['go_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) @property def LSBpa(self): - return 8 * self.sym_params['eps'] * self.params.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + if self.params.atemperature_params.T0 is None: + return None + else: + return 8 * self.sym_params['eps'] * self.params.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) @property def T4sbpgo(self): @@ -705,7 +717,7 @@ def _compute_tensor_dicts(self): return sy_arr_dic - def compute_tensor(self, ): + def compute_tensor(self): """Routine to compute the tensor.""" sy_arr_dic = self._compute_tensor_dicts() sy_arr_dic = self.remove_dic_zeros(sy_arr_dic) @@ -812,12 +824,20 @@ def subs_tensor(self, tensor=None, dict_opp=True): if isinstance(tensor, dict): ten_out = dict() for key in tensor.keys(): - ten_out[key] = tensor[key].subs(symbol_to_number_map) + val = tensor[key].subs(symbol_to_number_map) + try: + ten_out[key] = float(val) + except: + ten_out[key] = val elif dict_opp: ten_out = dict() for key in self.tensor_dic.keys(): - ten_out[key] = self.tensor_dic[key].subs(symbol_to_number_map) + val = self.tensor_dic[key].subs(symbol_to_number_map) + try: + ten_out[key] = float(val) + except: + ten_out[key] = val else: if tensor is not None: @@ -862,8 +882,10 @@ def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): subbed_tensor_sp = sp.COO.from_numpy(subbed_tensor_np) diff_arr = subbed_tensor_sp.todense() - numerical_tensor.tensor.todense() - self.print_tensor(diff_arr, tol) - + if np.sum(np.abs(diff_arr)) > tol: + self.print_tensor(diff_arr, tol) + raise ValueError("Symbolic tensor and numerical tensor do not match at the above coordinates") + def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): if tensor is None: if dict_opp: @@ -1028,9 +1050,9 @@ def _compute_stored_full_dict(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips._v[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), self.T4LSBpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -self.T4LSBpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), 4 * self.T4LSBpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.T4LSBpgo * val) if ground_temp: if self.T4LSBpgo is not None: @@ -1040,9 +1062,9 @@ def _compute_stored_full_dict(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips._v[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), self.T4LSBpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.T4LSBpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), 4 * self.T4LSBpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.T4LSBpgo * val) if ocean: #delta_T part @@ -1060,11 +1082,11 @@ def _compute_stored_full_dict(self): for m in range(nvar[3]): val = 0 for jj in range(nvar[3]): - val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] + val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), -self.T4sbpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), -4 * self.T4sbpgo * val) if ground_temp: # deltaT_g part @@ -1082,11 +1104,11 @@ def _compute_stored_full_dict(self): for m in range(nvar[2]): val = 0 for jj in range(nvar[2]): - val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] + val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), self.T4sbpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.T4sbpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), 4 * self.T4sbpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.T4sbpgo * val) return sy_arr_dic From f509edf514cd83b1361c166f619fb4059faf94b9 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 27 Jul 2023 17:08:17 +0200 Subject: [PATCH 029/143] Edit to structure of additional functions --- notebooks/Symbolic Output-Linear.ipynb | 52 +++-------- notebooks/Symbolic Output-dynamic_T.ipynb | 100 +++++++++++++++++++--- qgs/functions/symbolic_mul.py | 75 ++++++++++++++++ qgs/functions/symbolic_output.py | 71 +++++++++++++++ qgs/inner_products/symbolic.py | 44 ++++------ qgs/tensors/symbolic_qgtensor.py | 42 ++------- 6 files changed, 272 insertions(+), 112 deletions(-) create mode 100644 qgs/functions/symbolic_mul.py create mode 100644 qgs/functions/symbolic_output.py diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 31c4f52..a347c74 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -92,7 +92,8 @@ "source": [ "%%time\n", "# atm_ip = AtmosphericSymbolicInnerProducts(model_parameters)\n", - "# ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", + "# Takes ~5 mins\n", + "ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", "ocn_ip = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, @@ -106,7 +107,8 @@ "outputs": [], "source": [ "%%time\n", - "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", + "# Takes ~5 mins\n", + "atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, @@ -117,8 +119,8 @@ "metadata": {}, "outputs": [], "source": [ - "# atm_ip_stored.save_to_file('temp_atm_sym_lin.ip')\n", - "# ocn_ip_stored.save_to_file('temp_ocn_sym_lin.ip')" + "atm_ip_stored.save_to_file('temp_atm_sym_lin.ip')\n", + "ocn_ip_stored.save_to_file('temp_ocn_sym_lin.ip')" ] }, { @@ -216,16 +218,6 @@ "ten.compute_tensor()" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "425df2fd", - "metadata": {}, - "outputs": [], - "source": [ - "ten.tensor.shape" - ] - }, { "cell_type": "code", "execution_count": null, @@ -235,53 +227,33 @@ }, "outputs": [], "source": [ - "subs_list = ten.test_tensor_numerically(tol=1e-12)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3aa7adca", - "metadata": {}, - "outputs": [], - "source": [ - "ten.print_tensor(np.array(ten.subs_tensor()).astype(np.float64))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bd34e996", - "metadata": {}, - "outputs": [], - "source": [ - "ten.print_tensor(num_ten.tensor.todense())" + "ten.test_tensor_numerically(tol=1e-12)" ] }, { "cell_type": "code", "execution_count": null, - "id": "3526eac0", + "id": "6aabc02f", "metadata": {}, "outputs": [], "source": [ - "ten.tensor[2, 0, 3]" + "ten._set_symbolic_tensor()" ] }, { "cell_type": "code", "execution_count": null, - "id": "1817f522", + "id": "a60e7937", "metadata": {}, "outputs": [], "source": [ - "list(np.ndenumerate(ten.tensor))" + "ten.print_tensor(ten.tensor)" ] }, { "cell_type": "code", "execution_count": null, - "id": "45e42d95", + "id": "9c8c57bd", "metadata": {}, "outputs": [], "source": [] diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index 88f8192..0fe7e70 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -78,7 +78,7 @@ "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", - "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" + "from qgs.tensors.symbolic_qgtensor import _shift_dict_keys, _add_to_dict" ] }, { @@ -91,6 +91,7 @@ "outputs": [], "source": [ "%%time\n", + "# Takes ~5 mins\n", "# atm_ip = AtmosphericSymbolicInnerProducts(model_parameters)\n", "# ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", "ocn_ip = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" @@ -106,6 +107,7 @@ "outputs": [], "source": [ "%%time\n", + "# Takes ~5 mins\n", "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] @@ -179,61 +181,135 @@ { "cell_type": "code", "execution_count": null, - "id": "775c3dcd", + "id": "a64d4acf", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "_, _, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d0bd7dcc", "metadata": {}, "outputs": [], "source": [ - "ten.tensor.shape" + "ten_sub = ten.subs_tensor(ten.tensor_dic)" ] }, { "cell_type": "code", "execution_count": null, - "id": "7f40da39", + "id": "8ed132e4", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "_, _, num_ten = create_tendencies(model_parameters, return_qgtensor=True)" + "ten.test_tensor_numerically(tol=1e-12)" ] }, { "cell_type": "code", "execution_count": null, - "id": "8ed132e4", + "id": "bd34e996", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# ten.print_tensor(ten_num.tensor.todense())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6dd6324", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "atm_num = AtmosphericSymbolicInnerProducts(model_parameters, stored=True)\n", + "ocn_num = OceanicSymbolicInnerProducts(model_parameters, stored=True)\n", + "\n", + "ocn_num_ns = OceanicSymbolicInnerProducts(model_parameters, stored=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f314bcaa", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "ten.print_tensor(ocn_num._Z.todense())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45e42d95", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "subs_list = ten.test_tensor_numerically(tol=1e-12)" + "ten.print_tensor(ocn_loaded._Z)" ] }, { "cell_type": "code", "execution_count": null, - "id": "3aa7adca", + "id": "6fec84f6", "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(np.array(ten.subs_tensor()).astype(np.float64))" + "model_parameters.ground_basis is None" ] }, { "cell_type": "code", "execution_count": null, - "id": "bd34e996", + "id": "e38656d8", "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(num_ten.tensor.todense())" + "from qgs.functions.symbolic_output import create_symbolic_equations" ] }, { "cell_type": "code", "execution_count": null, - "id": "45e42d95", + "id": "c73f1cd9", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "# f, Df = create_symbolic_equations(model_parameters, atm)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e05a6a5d", + "metadata": {}, + "outputs": [], + "source": [ + "if atm_loaded.connect_to_ocean:\n", + " print(\"herllo\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc921587", "metadata": {}, "outputs": [], "source": [] diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py new file mode 100644 index 0000000..86666d9 --- /dev/null +++ b/qgs/functions/symbolic_mul.py @@ -0,0 +1,75 @@ +import sympy as sy + + +def _symbolic_tensordot(a, b, axes=2): + """ + Compute tensor dot product along specified axes of two sympy symbolic arrays + + This is based on numpy.tensordot + + Parameters + ---------- + a, b: sympy arrays + Tensors to "dot" + + axes: int + * integer_like + If an int N, sum over the last N axes of `a` and the first N axes + of `b` in order. The sizes of the corresponding axes must match. + + Returns + ------- + output: sympy tensor + The tensor dot product of the input + + """ + as_ = a.shape + nda = len(as_) + + a_com = [nda+i for i in range(-axes, 0)] + b_com = [nda+i for i in range(axes)] + sum_cols = tuple(a_com + b_com) + + prod = sy.tensorproduct(a, b) + + return sy.tensorcontraction(prod, sum_cols) + +def symbolic_sparse_mult2(dict, vec_a): + #//TODO: Complete documentation + res = sy.zeros((len(vec_a), len(vec_a))) + + for key in dict.keys(): + coo1, coo2, coo3 = key + res[coo1, coo2] += vec_a[coo3] * dict[key] + + return res + +def symbolic_sparse_mult3(dict, vec_a, vec_b): + #//TODO: Complete documentation + res = sy.zeros(len(vec_a)) + + for key in dict.keys(): + coo1, coo2, coo3 = key + res[coo1] += vec_a[coo2] * vec_b[coo3] * dict[key] + + return res + +def symbolic_sparse_mult4(dict, vec_a, vec_b, vec_c): + #//TODO: Complete documentation + res = sy.zeros((len(vec_a), len(vec_a))) + + for key in dict.keys(): + coo1, coo2, coo3, coo4, coo5 = key + res[coo1, coo2] += vec_a[coo3] * vec_b[coo4] * vec_c[coo5] * dict[key] + + return res + +def symbolic_sparse_mult5(dict, vec_a, vec_b, vec_c, vec_d): + #//TODO: Complete documentation + res = sy.zeros(len(vec_a)) + + for key in dict.keys(): + coo1, coo2, coo3, coo4, coo5 = key + res[coo1] += vec_a[coo2] * vec_b[coo3] * vec_c[coo4] * vec_d[coo5] * dict[key] + + return res \ No newline at end of file diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py new file mode 100644 index 0000000..f4157ec --- /dev/null +++ b/qgs/functions/symbolic_output.py @@ -0,0 +1,71 @@ +import numpy as np +import sympy as sy + +from qgs.functions.symbolic_mul import _symbolic_tensordot, symbolic_sparse_mult2, symbolic_sparse_mult3, symbolic_sparse_mult4, symbolic_sparse_mult5 + +from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, GroundSymbolicInnerProducts + +from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT + +def create_symbolic_equations(params, return_inner_products=False, return_symbolic_dict=False, return_symbolic_qgtensor=False): + """ + Function to output the raw symbolic functions of the qgs model. + """ + if params.atmospheric_basis is not None: + aip = AtmosphericSymbolicInnerProducts(params, return_symbolic=True) + else: + aip = None + + if params.oceanic_basis is not None: + oip = OceanicSymbolicInnerProducts(params, return_symbolic=True) + else: + oip = None + + if params.ground_basis is not None: + gip = GroundSymbolicInnerProducts(params, return_symbolic=True) + else: + gip = None + + if aip is not None and oip is not None: + if not aip.connected_to_ocean: + aip.connect_to_ocean(oip) + elif aip is not None and gip is not None: + if not aip.connected_to_ground: + aip.connect_to_ground(gip) + + if params.T4: + raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") + elif params.dynamic_T: + agotensor = SymbolicTensorDynamicT(params, aip, oip, gip) + else: + agotensor = SymbolicTensorLinear(params, aip, oip, gip) + + xx = list() + xx.append(sy.Symbol('1')) + + for i in range(1, params.ndim+1): + xx.append(sy.Symbol('U_'+str(i))) + + # Dont need to convert this to a sympy arr I think? + # xx = sy.tensor.array.ImmutableSparseNDimArray(xx, shape=(len(xx))) + + if params.dynamic_T: + func = symbolic_sparse_mult5(agotensor.tensor_dic, xx, xx, xx, xx) + Dfunc = symbolic_sparse_mult4(agotensor.jac_dic, xx, xx, xx) + + else: + func = symbolic_sparse_mult3(agotensor.tensor_dic, xx, xx) + Dfunc = symbolic_sparse_mult2(agotensor.jac_dic, xx) + + ret = list() + ret.append(func) + ret.append(Dfunc) + if return_inner_products: + ret.append((aip, oip, gip)) + if return_symbolic_dict: + ret.append(agotensor.tensor_dic) + if return_symbolic_qgtensor: + ret.append(agotensor.tensor) + return ret + + diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 0548098..f176c6e 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -884,7 +884,6 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) elif self._dynamic_T: # Z inner products - print("Running Z") args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.noc) for m in range(natm)] @@ -984,7 +983,6 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) elif self._dynamic_T: # V inner products - print("Running V") args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.noc) for m in range(self.noc)] @@ -1586,17 +1584,15 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False result_list[res[0]] = res[1] if permute: - while True: - try: - i = res[0][0] - print(res[0]) - idx = res[0][1:] - perm_idx = multiset_permutations(idx) - for perm in perm_idx: - idx = [i] + perm - result_list[tuple(idx)] = res[1] - except StopIteration: - break + i = res[0][0] + idx = res[0][1:] + perm_idx = multiset_permutations(idx) + for perm in perm_idx: + idx = [i] + perm + if subs is not None: + result_list[tuple(idx)] = res[1].subs(subs) + else: + result_list[tuple(idx)] = res[1] except StopIteration: break @@ -1607,9 +1603,13 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False future = pool.map(_num_apply, num_args_list) results = future.result() - if permute: - while True: - try: + + while True: + try: + res = next(results) + result_list[res[0]] = res[1] + + if permute: res = next(results) i = res[0][0] idx = res[0][1:] @@ -1617,15 +1617,9 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False for perm in perm_idx: idx = [i] + perm result_list[tuple(idx)] = res[1] - except StopIteration: - break - else: - while True: - try: - res = next(results) - result_list[res[0]] = res[1] - except StopIteration: - break + + except StopIteration: + break return result_list diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 7daca31..3a05086 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -8,6 +8,7 @@ from contextlib import redirect_stdout from qgs.functions.tendencies import create_tendencies +from qgs.functions.symbolic_mul import _symbolic_tensordot import numpy as np import sparse as sp @@ -830,7 +831,7 @@ def subs_tensor(self, tensor=None, dict_opp=True): except: ten_out[key] = val - elif dict_opp: + elif dict_opp and self.tensor_dic is not None: ten_out = dict() for key in self.tensor_dic.keys(): val = self.tensor_dic[key].subs(symbol_to_number_map) @@ -907,7 +908,11 @@ def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): bool_test = (v != 0) if bool_test: - print(str(ix) + ": " + str(v)) + try: + output_val = float(v) + except: + output_val = v + print(str(ix) + ": " + str(output_val)) class SymbolicTensorDynamicT(SymbolicTensorLinear): #//TODO: Need to work out symbolic tensor dot @@ -1302,39 +1307,6 @@ def _shift_dict_keys(dic, shift): shifted_dic[new_key] = dic[key] return shifted_dic - -def _symbolic_tensordot(a, b, axes=2): - """ - Compute tensor dot product along specified axes of two sympy symbolic arrays - - This is based on numpy.tensordot - - Parameters - ---------- - a, b: sympy arrays - Tensors to "dot" - - axes: int - * integer_like - If an int N, sum over the last N axes of `a` and the first N axes - of `b` in order. The sizes of the corresponding axes must match. - - Returns - ------- - output: sympy tensor - The tensor dot product of the input - - """ - as_ = a.shape - nda = len(as_) - - a_com = [nda+i for i in range(-axes, 0)] - b_com = [nda+i for i in range(axes)] - sum_cols = tuple(a_com + b_com) - - prod = sy.tensorproduct(a, b) - - return sy.tensorcontraction(prod, sum_cols) if __name__ == "__main__": dic = dict() From 3d094dcf740032077b6130e908b9561b79163249 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 3 Aug 2023 16:30:13 +0200 Subject: [PATCH 030/143] Dyn T tensor working - output of results draft --- notebooks/Symbolic Output-dynamic_T.ipynb | 103 ++++++++++++---------- qgs/functions/symbolic_mul.py | 42 +++++---- qgs/functions/symbolic_output.py | 89 ++++++++++++++++--- qgs/tensors/symbolic_qgtensor.py | 28 +++--- 4 files changed, 171 insertions(+), 91 deletions(-) diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index 0fe7e70..f934fbf 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -168,148 +168,157 @@ { "cell_type": "code", "execution_count": null, - "id": "cf668cb9", + "id": "a64d4acf", "metadata": { - "scrolled": false + "scrolled": true }, "outputs": [], "source": [ - "%%time\n", - "ten.compute_tensor()" + "_, _, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "a64d4acf", + "id": "8ed132e4", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "_, _, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" + "# ten.test_tensor_numerically(tol=1e-9)" ] }, { "cell_type": "code", "execution_count": null, - "id": "d0bd7dcc", - "metadata": {}, + "id": "c6dd6324", + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "ten_sub = ten.subs_tensor(ten.tensor_dic)" + "# atm_num = AtmosphericSymbolicInnerProducts(model_parameters, stored=True)\n", + "# ocn_num = OceanicSymbolicInnerProducts(model_parameters, stored=True)\n", + "\n", + "# ocn_num_ns = OceanicSymbolicInnerProducts(model_parameters, stored=False)" ] }, { "cell_type": "code", "execution_count": null, - "id": "8ed132e4", - "metadata": { - "scrolled": true - }, + "id": "6fec84f6", + "metadata": {}, "outputs": [], "source": [ - "ten.test_tensor_numerically(tol=1e-12)" + "model_parameters.ground_basis is None" ] }, { "cell_type": "code", "execution_count": null, - "id": "bd34e996", - "metadata": { - "scrolled": true - }, + "id": "e38656d8", + "metadata": {}, "outputs": [], "source": [ - "# ten.print_tensor(ten_num.tensor.todense())" + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations" ] }, { "cell_type": "code", "execution_count": null, - "id": "c6dd6324", - "metadata": { - "scrolled": true - }, + "id": "06ef5ad8", + "metadata": {}, "outputs": [], "source": [ - "atm_num = AtmosphericSymbolicInnerProducts(model_parameters, stored=True)\n", - "ocn_num = OceanicSymbolicInnerProducts(model_parameters, stored=True)\n", - "\n", - "ocn_num_ns = OceanicSymbolicInnerProducts(model_parameters, stored=False)" + "model_parameters.dynamic_T" ] }, { "cell_type": "code", "execution_count": null, - "id": "f314bcaa", + "id": "c73f1cd9", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "ten.print_tensor(ocn_num._Z.todense())" + "%%time\n", + "f = create_symbolic_equations(model_parameters, atm_loaded, ocn_loaded)" ] }, { "cell_type": "code", "execution_count": null, - "id": "45e42d95", - "metadata": { - "scrolled": true - }, + "id": "05754843", + "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(ocn_loaded._Z)" + "python_lang_translation = {\n", + " 'sqrt': 'math.sqrt',\n", + " 'pi': 'math.pi'\n", + "}" ] }, { "cell_type": "code", "execution_count": null, - "id": "6fec84f6", + "id": "ef01deb1", "metadata": {}, "outputs": [], "source": [ - "model_parameters.ground_basis is None" + "def translate_equations_my(equations, params, language='python'):\n", + "\n", + " _base_file_path = ''\n", + " if language == 'python':\n", + " file_ext = '.py'\n", + " base_file = _base_file_path + file_ext\n", + "\n", + " # translate mathematical operations\n", + " str_eq = dict()\n", + " for i in range(1, params.ndim):\n", + " print(i)\n", + " temp_str = str(equations[i])\n", + " for k in python_lang_translation.keys():\n", + " temp_str = temp_str.replace(k, python_lang_translation[k])\n", + " str_eq[i] = temp_str\n", + " return str_eq" ] }, { "cell_type": "code", "execution_count": null, - "id": "e38656d8", + "id": "685b443a", "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations" + "f[0]" ] }, { "cell_type": "code", "execution_count": null, - "id": "c73f1cd9", - "metadata": { - "scrolled": true - }, + "id": "2b316068", + "metadata": {}, "outputs": [], "source": [ - "# f, Df = create_symbolic_equations(model_parameters, atm)" + "translate_equations_my(f[0], model_parameters)" ] }, { "cell_type": "code", "execution_count": null, - "id": "e05a6a5d", + "id": "fba4bd18", "metadata": {}, "outputs": [], "source": [ - "if atm_loaded.connect_to_ocean:\n", - " print(\"herllo\")" + "len(f)" ] }, { "cell_type": "code", "execution_count": null, - "id": "bc921587", + "id": "415e4b38", "metadata": {}, "outputs": [], "source": [] diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py index 86666d9..758634f 100644 --- a/qgs/functions/symbolic_mul.py +++ b/qgs/functions/symbolic_mul.py @@ -1,5 +1,11 @@ import sympy as sy +def _add_to_dict(dic, loc, value): + try: + dic[loc] += value + except: + dic[loc] = value + return dic def _symbolic_tensordot(a, b, axes=2): """ @@ -34,42 +40,46 @@ def _symbolic_tensordot(a, b, axes=2): return sy.tensorcontraction(prod, sum_cols) -def symbolic_sparse_mult2(dict, vec_a): +def symbolic_sparse_mult2(dic, vec_a): #//TODO: Complete documentation - res = sy.zeros((len(vec_a), len(vec_a))) + res = dict() - for key in dict.keys(): + for key in dic.keys(): coo1, coo2, coo3 = key - res[coo1, coo2] += vec_a[coo3] * dict[key] + val = vec_a[coo3] * dic[key] + res = _add_to_dict(res, (coo1, coo2), val) return res -def symbolic_sparse_mult3(dict, vec_a, vec_b): +def symbolic_sparse_mult3(dic, vec_a, vec_b): #//TODO: Complete documentation - res = sy.zeros(len(vec_a)) + res = dict() - for key in dict.keys(): + for key in dic.keys(): coo1, coo2, coo3 = key - res[coo1] += vec_a[coo2] * vec_b[coo3] * dict[key] + val = vec_a[coo2] * vec_b[coo3] * dic[key] + res = _add_to_dict(res, coo1, val) return res -def symbolic_sparse_mult4(dict, vec_a, vec_b, vec_c): +def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): #//TODO: Complete documentation - res = sy.zeros((len(vec_a), len(vec_a))) + res = dict() - for key in dict.keys(): + for key in dic.keys(): coo1, coo2, coo3, coo4, coo5 = key - res[coo1, coo2] += vec_a[coo3] * vec_b[coo4] * vec_c[coo5] * dict[key] + val = vec_a[coo3] * vec_b[coo4] * vec_c[coo5] * dic[key] + res = _add_to_dict(res, (coo1, coo2), val) return res -def symbolic_sparse_mult5(dict, vec_a, vec_b, vec_c, vec_d): +def symbolic_sparse_mult5(dic, vec_a, vec_b, vec_c, vec_d): #//TODO: Complete documentation - res = sy.zeros(len(vec_a)) + res = dict() - for key in dict.keys(): + for key in dic.keys(): coo1, coo2, coo3, coo4, coo5 = key - res[coo1] += vec_a[coo2] * vec_b[coo3] * vec_c[coo4] * vec_d[coo5] * dict[key] + val = vec_a[coo2] * vec_b[coo3] * vec_c[coo4] * vec_d[coo5] * dic[key] + res = _add_to_dict(res, coo1, val) return res \ No newline at end of file diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index f4157ec..90f95c6 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -7,22 +7,48 @@ from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT -def create_symbolic_equations(params, return_inner_products=False, return_symbolic_dict=False, return_symbolic_qgtensor=False): +python_lang_translation = { + 'sqrt': 'math.sqrt', + 'pi': 'math.pi' +} + +fortran_lang_translation = { + '**': '^' +} + +julia_lang_translation = { + '**': '^', + 'pi': 'pi()' +} + +mathematica_lang_translation = { + '**': '^' +} + +def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, return_inner_products=False, return_jacobian=False, return_symbolic_dict=False, return_symbolic_qgtensor=False): """ Function to output the raw symbolic functions of the qgs model. """ if params.atmospheric_basis is not None: - aip = AtmosphericSymbolicInnerProducts(params, return_symbolic=True) + if atm_ip is None: + aip = AtmosphericSymbolicInnerProducts(params, return_symbolic=True) + else: aip = atm_ip else: aip = None if params.oceanic_basis is not None: - oip = OceanicSymbolicInnerProducts(params, return_symbolic=True) + if ocn_ip is None: + oip = OceanicSymbolicInnerProducts(params, return_symbolic=True) + else: + oip = ocn_ip else: oip = None if params.ground_basis is not None: - gip = GroundSymbolicInnerProducts(params, return_symbolic=True) + if gnd_ip is None: + gip = GroundSymbolicInnerProducts(params, return_symbolic=True) + else: + gip = gnd_ip else: gip = None @@ -41,25 +67,37 @@ def create_symbolic_equations(params, return_inner_products=False, return_symbol agotensor = SymbolicTensorLinear(params, aip, oip, gip) xx = list() - xx.append(sy.Symbol('1')) + # xx.append(sy.Symbol('1')) + xx.append(1) for i in range(1, params.ndim+1): xx.append(sy.Symbol('U_'+str(i))) - # Dont need to convert this to a sympy arr I think? - # xx = sy.tensor.array.ImmutableSparseNDimArray(xx, shape=(len(xx))) - if params.dynamic_T: - func = symbolic_sparse_mult5(agotensor.tensor_dic, xx, xx, xx, xx) - Dfunc = symbolic_sparse_mult4(agotensor.jac_dic, xx, xx, xx) + eq = symbolic_sparse_mult5(agotensor.tensor_dic, xx, xx, xx, xx) + if return_jacobian: + Deq = symbolic_sparse_mult4(agotensor.jac_dic, xx, xx, xx) else: - func = symbolic_sparse_mult3(agotensor.tensor_dic, xx, xx) - Dfunc = symbolic_sparse_mult2(agotensor.jac_dic, xx) + eq = symbolic_sparse_mult3(agotensor.tensor_dic, xx, xx) + if return_jacobian: + Deq = symbolic_sparse_mult2(agotensor.jac_dic, xx) + + eq_simplified = dict() + Deq_simplified = dict() + + #//TODO: This section using .simplify() is super slow. + for i in range(1, params.ndim+1): + eq_simplified[i] = eq[i].simplify() + if return_jacobian: + for j in range(1, params.ndim+1): + if (i, j) in Deq: + Deq_simplified[(i, j)] = Deq[(i, j)].simplify() ret = list() - ret.append(func) - ret.append(Dfunc) + ret.append(eq_simplified) + if return_jacobian: + ret.append(Deq_simplified) if return_inner_products: ret.append((aip, oip, gip)) if return_symbolic_dict: @@ -68,4 +106,27 @@ def create_symbolic_equations(params, return_inner_products=False, return_symbol ret.append(agotensor.tensor) return ret +def translate_equations(equations, params, language='python'): + ''' + Function to output the model equations as a string in the specified language. The following languages that this is set up for is: + - Python + - Fortran + - Julia + - Mathematica + ''' + + #//TODO: Finish this function + _base_file_path = '' + if language == 'python': + file_ext = '.py' + base_file = _base_file_path + file_ext + # translate mathematical operations + str_eq = dict() + for i in range(1, params.ndim): + temp_str = str(equations[i]) + #//TODO: This only works for single array, not jacobian + for k in python_lang_translation.keys(): + temp_str = temp_str.replace(k, python_lang_translation[k]) + str_eq[i] = temp_str + return str_eq \ No newline at end of file diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 3a05086..70cac13 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -8,7 +8,7 @@ from contextlib import redirect_stdout from qgs.functions.tendencies import create_tendencies -from qgs.functions.symbolic_mul import _symbolic_tensordot +from qgs.functions.symbolic_mul import _add_to_dict, _symbolic_tensordot import numpy as np import sparse as sp @@ -71,7 +71,8 @@ def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_p self.params.symbolic_insolation_array() self.params.symbolic_orography_array() - # self.compute_tensor() + if not(self.params.dynamic_T): + self.compute_tensor() def _psi_a(self, i): """Transform the :math:`\\psi_{\\mathrm a}` :math:`i`-th coefficient into the effective model's variable. @@ -883,9 +884,12 @@ def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): subbed_tensor_sp = sp.COO.from_numpy(subbed_tensor_np) diff_arr = subbed_tensor_sp.todense() - numerical_tensor.tensor.todense() - if np.sum(np.abs(diff_arr)) > tol: + + total_error = np.sum(np.abs(diff_arr)) + if total_error > tol: self.print_tensor(diff_arr, tol) - raise ValueError("Symbolic tensor and numerical tensor do not match at the above coordinates") + + raise ValueError("Symbolic tensor and numerical tensor do not match at the above coordinates, with a total error of: " + str(total_error)) def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): if tensor is None: @@ -955,6 +959,9 @@ class SymbolicTensorDynamicT(SymbolicTensorLinear): def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) + + if params.dynamic_T: + self.compute_tensor() def _compute_tensor_dicts(self): @@ -1028,7 +1035,7 @@ def _compute_stored_full_dict(self): for j in range(nvar[2]): U_inv[i, j] = bips.U(i, j) U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(U_inv) + U_inv = U_inv.inverse() ################# @@ -1089,9 +1096,9 @@ def _compute_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), -self.T4sbpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), - self.T4sbpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), -4 * self.T4sbpgo * val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.T4sbpgo * val) if ground_temp: # deltaT_g part @@ -1282,13 +1289,6 @@ def _kronecker_delta(i, j): else: return 0 - -def _add_to_dict(dic, loc, value): - try: - dic[loc] += value - except: - dic[loc] = value - return dic def _shift_dict_keys(dic, shift): """ From 746126ae5a2cf11bcb30bca597b75ece7c8b9703 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 4 Aug 2023 15:26:01 +0200 Subject: [PATCH 031/143] First working equation outputs --- notebooks/Symbolic Output-dynamic_T.ipynb | 182 +++++++++++++++----- qgs/functions/symbolic_output.py | 195 +++++++++++++++++++--- qgs/params/params.py | 1 + qgs/tensors/symbolic_qgtensor.py | 4 +- 4 files changed, 320 insertions(+), 62 deletions(-) diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index f934fbf..431ef55 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -207,118 +207,220 @@ { "cell_type": "code", "execution_count": null, - "id": "6fec84f6", + "id": "e38656d8", "metadata": {}, "outputs": [], "source": [ - "model_parameters.ground_basis is None" + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, print_equations, equation_as_function" ] }, { "cell_type": "code", "execution_count": null, - "id": "e38656d8", + "id": "c73f1cd9", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "funcs = create_symbolic_equations(model_parameters, atm_loaded, ocn_loaded)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27797dd9", "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations" + "model_parameters._set_symbolic_parameters()" ] }, { "cell_type": "code", "execution_count": null, - "id": "06ef5ad8", + "id": "e880cb26", "metadata": {}, "outputs": [], "source": [ - "model_parameters.dynamic_T" + "%%time\n", + "eqs = print_equations(funcs[0], model_parameters, subs=True, return_equations=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "c73f1cd9", - "metadata": { - "scrolled": true - }, + "id": "12a879bc", + "metadata": {}, "outputs": [], "source": [ "%%time\n", - "f = create_symbolic_equations(model_parameters, atm_loaded, ocn_loaded)" + "fx = equation_as_function(funcs[0], model_parameters, subs=True, language='python', string_output=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "05754843", + "id": "bccb3e42", "metadata": {}, "outputs": [], "source": [ - "python_lang_translation = {\n", - " 'sqrt': 'math.sqrt',\n", - " 'pi': 'math.pi'\n", - "}" + "for i in fx:\n", + " print(i)" ] }, { "cell_type": "code", "execution_count": null, - "id": "ef01deb1", + "id": "5e14f2e2", "metadata": {}, "outputs": [], "source": [ - "def translate_equations_my(equations, params, language='python'):\n", - "\n", - " _base_file_path = ''\n", - " if language == 'python':\n", - " file_ext = '.py'\n", - " base_file = _base_file_path + file_ext\n", - "\n", - " # translate mathematical operations\n", - " str_eq = dict()\n", - " for i in range(1, params.ndim):\n", - " print(i)\n", - " temp_str = str(equations[i])\n", - " for k in python_lang_translation.keys():\n", - " temp_str = temp_str.replace(k, python_lang_translation[k])\n", - " str_eq[i] = temp_str\n", - " return str_eq" + "from numba import njit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9266e59c", + "metadata": {}, + "outputs": [], + "source": [ + "@njit\n", + "def f(t, U):\n", + " #Tendency function of the qgs model\n", + " U_out = np.empty_like(U)\n", + " U_out[0] = -0.05*U[0] + 0.05*U[11] + 0.168986428105931*U[22] + 0.251005505082614*U[24]\n", + " U_out[1] = -0.980418808722262*U[0]*U[2] - 0.980418808722262*U[11]*U[13] + 0.05*U[12] - 1.56867009395562*U[14]*U[16] - 1.50055762081784*U[15]*U[18] + 1.50055762081784*U[16]*U[17] - 0.05*U[1] - 0.0112217054051163*U[21] + 0.120745727252785*U[2] - 1.56867009395562*U[3]*U[5] - 1.50055762081784*U[4]*U[7] + 1.50055762081784*U[5]*U[6]\n", + " U_out[2] = 0.980418808722262*U[0]*U[1] + 0.980418808722262*U[11]*U[12] + 0.05*U[13] + 1.56867009395562*U[14]*U[15] + 1.50055762081784*U[15]*U[17] + 1.50055762081784*U[16]*U[18] - 0.120745727252785*U[1] + 0.05*U[25] - 0.05*U[2] + 1.56867009395562*U[3]*U[4] + 1.50055762081784*U[4]*U[6] + 1.50055762081784*U[5]*U[7]\n", + " U_out[3] = 1.87265793760678*U[12]*U[16] - 1.87265793760678*U[13]*U[15] + 0.05*U[14] + 3.74531587521356*U[17]*U[20] - 3.74531587521356*U[18]*U[19] + 1.87265793760678*U[1]*U[5] - 0.00679432430697251*U[21] + 0.0810088839426413*U[23] - 1.87265793760678*U[2]*U[4] - 0.05*U[3] + 3.74531587521356*U[6]*U[9] - 3.74531587521356*U[7]*U[8]\n", + " U_out[4] = -1.02902937637678*U[0]*U[5] - 1.02902937637678*U[11]*U[16] + 1.73752196836555*U[12]*U[18] + 0.574852231579352*U[13]*U[14] - 1.73752196836555*U[13]*U[17] + 0.05*U[15] + 1.73752196836555*U[1]*U[7] - 0.0164935614721478*U[22] + 0.574852231579352*U[2]*U[3] - 1.73752196836555*U[2]*U[6] - 0.05*U[4] + 0.0570836566449895*U[5]\n", + " U_out[5] = 1.02902937637678*U[0]*U[4] + 1.02902937637678*U[11]*U[15] - 0.574852231579352*U[12]*U[14] - 1.73752196836555*U[12]*U[17] - 1.73752196836555*U[13]*U[18] + 0.05*U[16] - 0.574852231579352*U[1]*U[3] - 1.73752196836555*U[1]*U[6] + 0.05*U[26] - 1.73752196836555*U[2]*U[7] - 0.0570836566449895*U[4] - 0.05*U[5]\n", + " U_out[6] = -2.71889339738442*U[0]*U[7] - 2.71889339738442*U[11]*U[18] + 0.753865979381443*U[12]*U[16] + 0.753865979381443*U[13]*U[15] - 4.35022943581506*U[14]*U[20] + 0.05*U[17] + 0.753865979381443*U[1]*U[5] - 0.000777999678859866*U[21] + 0.753865979381443*U[2]*U[4] - 4.35022943581506*U[3]*U[9] - 0.05*U[6] + 0.0837128882242243*U[7]\n", + " U_out[7] = 2.71889339738442*U[0]*U[6] + 2.71889339738442*U[11]*U[17] - 0.753865979381443*U[12]*U[15] + 0.753865979381443*U[13]*U[16] + 4.35022943581506*U[14]*U[19] + 0.05*U[18] - 0.753865979381443*U[1]*U[4] + 0.753865979381443*U[2]*U[5] + 4.35022943581506*U[3]*U[8] - 0.0837128882242243*U[6] - 0.05*U[7]\n", + " U_out[8] = -2.26482546109568*U[0]*U[9] - 2.26482546109568*U[11]*U[20] - 1.7450294536311*U[14]*U[18] + 0.05*U[19] - 0.0017443933973331*U[22] - 1.7450294536311*U[3]*U[7] - 0.05*U[8] + 0.0603728636263923*U[9]\n", + " U_out[9] = 2.26482546109568*U[0]*U[8] + 2.26482546109568*U[11]*U[19] + 1.7450294536311*U[14]*U[17] + 0.05*U[20] + 1.7450294536311*U[3]*U[6] - 0.0603728636263923*U[8] - 0.05*U[9]\n", + " U_out[10] = -0.00055453242558364*U[10]**4 - 0.0193798449612403*U[10] - 0.00576517534924902*U[21] - 0.0127293180382657*U[23] + 1.73291382994887e-5*U[29]**4 + 5.6185881728191e-5*U[29]**3*U[30] + 1.87286272427303e-5*U[29]**3*U[32] + 0.00968992248062015*U[29] + 0.00785435532111145*U[30] + 0.00261811844037048*U[32] + 0.000515537550678319\n", + " U_out[11] = 0.00454545454545455*U[0] - 0.00201648154757687*U[10]**3*U[11] - 0.0239816772374912*U[11] - 1.41868025576271*U[12]*U[2] + 1.41868025576271*U[13]*U[1] - 1.13494420461017*U[15]*U[5] + 1.13494420461017*U[16]*U[4] - 2.83736051152543*U[17]*U[7] + 2.83736051152543*U[18]*U[6] - 2.26988840922034*U[19]*U[9] + 2.26988840922034*U[20]*U[8] - 0.0153624025550847*U[22] - 0.0228186822802377*U[24] + 4.81568702750899e-5*U[29]**3*U[31] + 1.9262748110036e-5*U[29]**3*U[33] + 0.00673196110231097*U[31] + 0.00269278444092439*U[33] + 0.000468670500616653\n", + " U_out[12] = -1.43757363347933*U[0]*U[13] - 0.00174793514762377*U[10]**3*U[12] + 1.02191932371371*U[11]*U[2] - 0.030110200915083*U[12] + 0.0255954299692664*U[13] + 1.63507091794193*U[14]*U[5] + 1.21855791962175*U[15]*U[7] - 2.30011781356693*U[16]*U[3] - 1.21855791962175*U[16]*U[6] + 1.85472813238771*U[17]*U[5] - 1.85472813238771*U[18]*U[4] + 0.0105988967691095*U[1] + 0.00237875394324372*U[21] - 2.31827099123654e-5*U[29]**3*U[30] - 0.00324076503486835*U[30]\n", + " U_out[13] = 1.43757363347933*U[0]*U[12] - 0.00174793514762377*U[10]**3*U[13] - 1.02191932371371*U[11]*U[1] - 0.0255954299692664*U[12] - 0.030110200915083*U[13] - 1.63507091794193*U[14]*U[4] + 2.30011781356693*U[15]*U[3] - 1.21855791962175*U[15]*U[6] - 1.21855791962175*U[16]*U[7] + 1.85472813238771*U[17]*U[4] + 1.85472813238771*U[18]*U[5] - 0.0105988967691095*U[25] + 5.46229733632427e-5*U[29]**3*U[34] + 0.0105988967691095*U[2] + 0.00763587271916482*U[34]\n", + " U_out[14] = -0.0015843783588104*U[10]**3*U[14] - 1.24843862507119*U[12]*U[5] + 1.24843862507119*U[13]*U[4] - 0.0338427464008859*U[14] - 2.3185288751322*U[15]*U[2] + 2.3185288751322*U[16]*U[1] - 2.49687725014238*U[17]*U[9] + 2.49687725014238*U[18]*U[8] - 4.63705775026441*U[19]*U[7] + 4.63705775026441*U[20]*U[6] + 0.00194123551627786*U[21] - 0.0231453954121832*U[23] - 1.89187704652139e-5*U[29]**3*U[30] + 3.4053786837385e-5*U[29]**3*U[32] - 0.00264469900447931*U[30] + 0.00476045820806276*U[32] + 0.0142857142857143*U[3]\n", + " U_out[15] = -1.16886956037576*U[0]*U[16] - 0.00141372192628079*U[10]**3*U[15] + 0.422511733532696*U[11]*U[5] - 0.612715105162524*U[12]*U[7] - 1.38291034440645*U[13]*U[3] + 0.612715105162524*U[13]*U[6] + 1.79985224341047*U[14]*U[2] - 0.0377373135508224*U[15] + 0.0207014663040147*U[16] - 1.87294455066922*U[17]*U[2] + 1.87294455066922*U[18]*U[1] + 0.00598141266899433*U[22] - 1.87500693937487e-5*U[29]**3*U[31] - 0.00262111588862201*U[31] + 0.0181325685149777*U[4]\n", + " U_out[16] = 1.16886956037576*U[0]*U[15] - 0.00141372192628079*U[10]**3*U[16] - 0.422511733532696*U[11]*U[4] + 1.38291034440645*U[12]*U[3] + 0.612715105162524*U[12]*U[6] + 0.612715105162524*U[13]*U[7] - 1.79985224341047*U[14]*U[1] - 0.0207014663040147*U[15] - 0.0377373135508224*U[16] - 1.87294455066922*U[17]*U[1] - 1.87294455066922*U[18]*U[2] - 0.0181325685149777*U[26] + 4.41788101962747e-5*U[29]**3*U[35] + 0.0061758588149268*U[35] + 0.0181325685149777*U[5]\n", + " U_out[17] = -2.94535914360826*U[0]*U[18] - 0.00124894690446766*U[10]**3*U[17] + 0.569389237785845*U[11]*U[7] - 0.768581081081081*U[12]*U[5] - 0.768581081081081*U[13]*U[4] + 0.911022780457353*U[14]*U[9] + 1.42736486486486*U[15]*U[2] + 1.42736486486486*U[16]*U[1] - 0.0414976604511488*U[17] + 0.0365772529628368*U[18] - 4.71257462977322*U[20]*U[3] + 0.000339936796618951*U[21] - 3.31293455842248e-6*U[29]**3*U[30] - 0.000463122841131525*U[30] + 0.0218468468468468*U[6]\n", + " U_out[18] = 2.94535914360826*U[0]*U[17] - 0.00124894690446766*U[10]**3*U[18] - 0.569389237785845*U[11]*U[6] + 0.768581081081081*U[12]*U[4] - 0.768581081081081*U[13]*U[5] - 0.911022780457353*U[14]*U[8] - 1.42736486486486*U[15]*U[1] + 1.42736486486486*U[16]*U[2] - 0.0365772529628368*U[17] - 0.0414976604511488*U[18] + 4.71257462977322*U[19]*U[3] + 0.0218468468468468*U[7]\n", + " U_out[19] = -2.37660377951895*U[0]*U[20] - 0.00106846324775268*U[10]**3*U[19] + 0.0288656329496229*U[11]*U[9] + 1.50101291338039*U[14]*U[7] - 3.30992591155675*U[18]*U[3] - 0.0456164956460695*U[19] + 0.031291522765895*U[20] + 0.000904126828290182*U[22] - 2.83418678986432e-6*U[29]**3*U[31] - 0.000396197575072056*U[31] + 0.0259152215799615*U[8]\n", + " U_out[20] = 2.37660377951895*U[0]*U[19] - 0.00106846324775268*U[10]**3*U[20] - 0.0288656329496229*U[11]*U[8] - 1.50101291338039*U[14]*U[6] + 3.30992591155675*U[17]*U[3] - 0.031291522765895*U[19] - 0.0456164956460695*U[20] + 0.0259152215799615*U[9]\n", + " U_out[21] = 6.35094981132412e-8*U[12] + 8.50239961075093e-8*U[14] + 3.66419111790894e-8*U[17] - 6.35094981132412e-8*U[1] - 1.58263150747531e-7*U[21] - 0.000969739516006511*U[22]*U[25] - 0.00116937805032627*U[22]*U[27] - 0.00278561057700379*U[23]*U[26] - 0.00308506837848344*U[23]*U[28] - 0.00534779391583196*U[24]*U[27] - 7.91389255433123e-5*U[25] - 8.50239961075093e-8*U[3] - 3.66419111790894e-8*U[6]\n", + " U_out[22] = 4.2438907259419e-8*U[0] - 4.2438907259419e-8*U[11] + 1.34106967724862e-7*U[15] + 5.07202450868017e-8*U[19] - 0.000708243330589963*U[21]*U[25] + 0.00172613939940631*U[21]*U[27] - 4.91188341087484e-7*U[22] - 0.00626988589506499*U[23]*U[25] - 0.0119940379417069*U[24]*U[26] - 7.9002862144845e-5*U[26] - 1.34106967724862e-7*U[4] - 5.07202450868017e-8*U[8]\n", + " U_out[23] = -1.52343526260616e-7*U[14] - 0.00317032731211854*U[21]*U[26] + 0.0060425658523595*U[21]*U[28] + 0.00160884360522711*U[22]*U[25] - 1.04352693785129e-6*U[23] - 0.0178532887343688*U[24]*U[25] - 7.87771265478973e-5*U[27] + 1.52343526260616e-7*U[3]\n", + " U_out[24] = 1.68596160851496e-8*U[0] - 1.68596160851496e-8*U[11] - 0.00857174744319731*U[21]*U[27] - 0.00140681174654471*U[22]*U[26] + 0.00871788567853858*U[23]*U[25] - 1.8115193420227e-6*U[24] - 7.84632552652771e-5*U[28]\n", + " U_out[25] = -1.49531921957748e-7*U[13] + 0.00167798163428125*U[21]*U[22] + 7.9081381622411e-5*U[21] + 0.0046610600952257*U[22]*U[23] + 0.00913567778664237*U[23]*U[24] - 2.99063843915495e-7*U[25] + 1.49531921957748e-7*U[2]\n", + " U_out[26] = -3.15752734584711e-7*U[16] + 0.0059559067658186*U[21]*U[23] + 0.0134007902230919*U[22]*U[24] + 7.89455158524528e-5*U[22] - 6.31505469169422e-7*U[26] + 3.15752734584711e-7*U[5]\n", + " U_out[27] = -0.0005567719890759*U[21]*U[22] + 0.0139192997268975*U[21]*U[24] + 7.8720107381223e-5*U[23] - 1.18304364206348e-6*U[27]\n", + " U_out[28] = -0.00295762797162433*U[21]*U[23] + 7.84066893926385e-5*U[24] - 1.94992690884148e-6*U[28]\n", + " U_out[29] = 2.7726621279182e-5*U[10]**4 + 0.000141323097779036*U[10]**3*U[12] + 5.0894196312133e-5*U[10]**3*U[14] + 2.82646195558073e-5*U[10]**3*U[17] + 0.00193798449615121*U[10] + 0.00246948564053933*U[12] + 0.000889327285869774*U[14] + 0.000493897128107866*U[17] + 1.30103637962932*U[21]*U[35] - 0.650518189814661*U[21]*U[37] - 1.30103637961282*U[22]*U[34] + 0.975777284716489*U[22]*U[36] - 3.90310913886596*U[23]*U[35] + 1.95155456943298*U[23]*U[37] - 2.27681366435131*U[24]*U[34] - 4.87888642358245*U[24]*U[36] + 1.30103637961282*U[25]*U[31] + 2.27681366435131*U[25]*U[33] - 1.30103637962932*U[26]*U[30] + 3.90310913886596*U[26]*U[32] - 0.975777284716489*U[27]*U[31] + 4.87888642358245*U[27]*U[33] + 0.650518189814661*U[28]*U[30] - 1.95155456943298*U[28]*U[32] - 2.28014977624852e-6*U[29]**4 + 8.470329472543e-22*U[29]**3*U[30] + 2.5410988417629e-21*U[29]**3*U[32] - 0.000968992248062015*U[29] + 0.000180438142739942\n", + " U_out[30] = -3.3881317890172e-21*U[10]**4 - 0.000161622362549857*U[10]**3*U[12] - 8.36313275290456e-5*U[10]**3*U[14] - 3.23244725099714e-5*U[10]**3*U[17] - 3.96655364803822e-16*U[10] - 0.00282419583055875*U[12] - 0.00146137726726263*U[14] - 0.000564839166111749*U[17] - 1.05458036756628*U[21]*U[35] + 0.52729018378314*U[21]*U[37] + 2.02958036756658*U[22]*U[34] - 1.11593527567481*U[22]*U[36] + 4.46374110269924*U[23]*U[35] - 2.23187055134962*U[23]*U[37] + 1.84551564324099*U[24]*U[34] + 5.57967637837405*U[24]*U[36] - 2.02958036756658*U[25]*U[31] - 1.84551564324099*U[25]*U[33] + 1.05458036756628*U[26]*U[30] - 4.46374110269924*U[26]*U[32] + 1.11593527567481*U[27]*U[31] - 5.57967637837405*U[27]*U[33] - 0.52729018378314*U[28]*U[30] + 2.23187055134962*U[28]*U[32] - 8.470329472543e-22*U[29]**4 - 9.12059910499407e-6*U[29]**3*U[30] - 2.5410988417629e-21*U[29]**3*U[32] - 0.000968992248062015*U[30] - 3.68628738645072e-17\n", + " U_out[31] = 8.47560916841583e-5*U[10]**3*U[11] - 4.70701742060666e-5*U[10]**3*U[15] - 9.41403484121332e-6*U[10]**3*U[19] + 0.00148103144250841*U[11] - 0.000822506165849588*U[15] - 0.000164501233169918*U[19] - 0.975*U[21]*U[34] + 0.325*U[21]*U[36] + 1.625*U[23]*U[34] + 1.95*U[24]*U[35] + 0.975*U[25]*U[30] - 1.625*U[25]*U[32] - 1.95*U[26]*U[33] - 0.325*U[27]*U[30] - 9.12059910499408e-6*U[29]**3*U[31] - 0.000968992248062015*U[31] + 0.000137893034416115\n", + " U_out[32] = -3.3881317890172e-21*U[10]**4 - 3.81840627812635e-5*U[10]**3*U[12] + 6.25293886200869e-5*U[10]**3*U[14] - 7.63681255625269e-6*U[10]**3*U[17] - 2.16840434497101e-19*U[10] - 0.0006672298882363*U[12] + 0.00109264111625484*U[14] - 0.00013344597764726*U[17] - 1.65152678918876*U[21]*U[35] + 0.82576339459438*U[21]*U[37] - 1.27347321081124*U[22]*U[34] - 0.26364509189157*U[22]*U[36] + 1.05458036756628*U[23]*U[35] - 0.527290183783139*U[23]*U[37] + 2.89017188108033*U[24]*U[34] + 1.31822545945785*U[24]*U[36] + 1.27347321081124*U[25]*U[31] - 2.89017188108033*U[25]*U[33] + 1.65152678918876*U[26]*U[30] - 1.05458036756628*U[26]*U[32] + 0.26364509189157*U[27]*U[31] - 1.31822545945785*U[27]*U[33] - 0.82576339459438*U[28]*U[30] + 0.527290183783139*U[28]*U[32] + 2.11758236813575e-22*U[29]**4 - 9.12059910499408e-6*U[29]**3*U[32] - 0.000968992248062015*U[32] + 2.71050543121376e-20\n", + " U_out[33] = 3.39024366736633e-5*U[10]**3*U[11] + 0.000592412577003366*U[11] - 1.625*U[21]*U[36] - 1.95*U[22]*U[35] - 2.275*U[23]*U[34] + 2.275*U[25]*U[32] + 1.95*U[26]*U[31] + 1.625*U[27]*U[30] - 9.12059910499408e-6*U[29]**3*U[33] - 0.000968992248062015*U[33] + 5.51572137664459e-5\n", + " U_out[34] = 0.000110906485116728*U[10]**3*U[13] + 0.00193798449612403*U[13] + 0.975*U[21]*U[31] - 0.975*U[22]*U[30] + 1.625*U[22]*U[32] - 1.625*U[23]*U[31] + 2.275*U[23]*U[33] - 2.275*U[24]*U[32] - 9.12059910499408e-6*U[29]**3*U[34] - 0.000968992248062015*U[34]\n", + " U_out[35] = 0.000110906485116728*U[10]**3*U[16] + 0.00193798449612403*U[16] + 1.3*U[21]*U[32] + 1.95*U[22]*U[33] - 1.3*U[23]*U[30] - 1.95*U[24]*U[31] - 9.12059910499408e-6*U[29]**3*U[35] - 0.000968992248062015*U[35]\n", + " U_out[36] = -0.325*U[21]*U[31] + 1.625*U[21]*U[33] + 0.325*U[22]*U[30] - 1.625*U[24]*U[30] - 9.12059910499408e-6*U[29]**3*U[36] - 0.000968992248062015*U[36]\n", + " U_out[37] = -0.65*U[21]*U[32] + 0.65*U[23]*U[30] - 9.12059910499408e-6*U[29]**3*U[37] - 0.000968992248062015*U[37]\n", + " return U_out" ] }, { "cell_type": "code", "execution_count": null, - "id": "685b443a", + "id": "cd4adc73", "metadata": {}, "outputs": [], "source": [ - "f[0]" + "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", + "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": null, - "id": "2b316068", + "id": "060ee686", "metadata": {}, "outputs": [], "source": [ - "translate_equations_my(f[0], model_parameters)" + "integrator = RungeKuttaIntegrator()\n", + "integrator.set_func(f)" ] }, { "cell_type": "code", "execution_count": null, - "id": "fba4bd18", + "id": "3297ea79", "metadata": {}, "outputs": [], "source": [ - "len(f)" + "%%time\n", + "ic = np.random.rand(model_parameters.ndim)*0.1\n", + "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=0)\n", + "time, ic = integrator.get_trajectories()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "919f59cc", + "metadata": {}, + "outputs": [], + "source": [ + "ic = np.array([\n", + " 4.64467907e-02, -2.25054437e-02, -3.51762104e-02, 4.65057475e-03,\n", + " -5.08842464e-03, 1.92789965e-02, 3.47849539e-03, 1.86038254e-02,\n", + " -2.99742925e-03, 8.85862363e-03, 1.53577181e+00, 4.29584574e-02,\n", + " -1.88143295e-02, -1.15114614e-02, 1.24843108e-03, -3.61467108e-03,\n", + " 2.81459020e-03, 5.59630451e-03, 3.56804517e-03, -1.52654934e-03,\n", + " 3.09046486e-03, 1.18185262e-06, 6.76756261e-04, 4.21981618e-06,\n", + " 1.06290914e-05, -1.64056994e-05, 3.40932989e-05, 1.90943692e-05,\n", + " 1.08189600e-05, 3.16661484e+00, -3.06749318e-04, 1.82036792e-01,\n", + " -1.33924039e-04, 9.77967977e-03, -5.58853689e-03, 2.33218135e-02,\n", + " 3.45971396e-05, -5.88894363e-05\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76c9285a", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", + "reference_time, reference_traj = integrator.get_trajectories()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb7b11cf", + "metadata": {}, + "outputs": [], + "source": [ + "varx = 22\n", + "vary = 31\n", + "plt.figure(figsize=(10, 8))\n", + "\n", + "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", + "\n", + "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" ] }, { "cell_type": "code", "execution_count": null, - "id": "415e4b38", + "id": "45fc0093", "metadata": {}, "outputs": [], "source": [] diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 90f95c6..bfe4d1d 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -25,7 +25,7 @@ '**': '^' } -def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, return_inner_products=False, return_jacobian=False, return_symbolic_dict=False, return_symbolic_qgtensor=False): +def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, simplify=False, return_inner_products=False, return_jacobian=False, return_symbolic_dict=False, return_symbolic_qgtensor=False): """ Function to output the raw symbolic functions of the qgs model. """ @@ -67,7 +67,6 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, ret agotensor = SymbolicTensorLinear(params, aip, oip, gip) xx = list() - # xx.append(sy.Symbol('1')) xx.append(1) for i in range(1, params.ndim+1): @@ -87,12 +86,17 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, ret Deq_simplified = dict() #//TODO: This section using .simplify() is super slow. - for i in range(1, params.ndim+1): - eq_simplified[i] = eq[i].simplify() + if simplify: + for i in range(1, params.ndim+1): + eq_simplified[i] = eq[i].simplify() + if return_jacobian: + for j in range(1, params.ndim+1): + if (i, j) in Deq: + Deq_simplified[(i, j)] = Deq[(i, j)].simplify() + else: + eq_simplified = eq if return_jacobian: - for j in range(1, params.ndim+1): - if (i, j) in Deq: - Deq_simplified[(i, j)] = Deq[(i, j)].simplify() + Deq_simplified = Deq ret = list() ret.append(eq_simplified) @@ -106,27 +110,178 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, ret ret.append(agotensor.tensor) return ret -def translate_equations(equations, params, language='python'): +def translate_equations(equations, language='python'): ''' - Function to output the model equations as a string in the specified language. The following languages that this is set up for is: + Function to output the model equations as a string in the specified language. The languages that are availible to the user are: - Python - Fortran - Julia - Mathematica ''' - #//TODO: Finish this function - _base_file_path = '' if language == 'python': - file_ext = '.py' - base_file = _base_file_path + file_ext + translator = python_lang_translation + + if language == 'julia': + translator = julia_lang_translation - # translate mathematical operations + # translate mathematical operations + if isinstance(equations, dict): str_eq = dict() - for i in range(1, params.ndim): - temp_str = str(equations[i]) + for key in equations.keys(): + temp_str = str(equations[key]) #//TODO: This only works for single array, not jacobian - for k in python_lang_translation.keys(): - temp_str = temp_str.replace(k, python_lang_translation[k]) - str_eq[i] = temp_str - return str_eq \ No newline at end of file + for k in translator.keys(): + temp_str = temp_str.replace(k, translator[k]) + str_eq[key] = temp_str + else: + temp_str = str(equations) + for k in translator.keys(): + temp_str = temp_str.replace(k, translator[k]) + str_eq = temp_str + + return str_eq + +def print_equations(equations, params, save_loc=None, language='python', subs=None, return_equations=False): + ''' + Function prints the equations as strings, in the programming language specified, and saves the equations to the specified location. + The variables in the equation are substituted if the model variable is input. + + Parameters + ---------- + equations: dictionary of symbolic expressions + + params: qgs model params + + save_loc: String, location to save the outputs as a .txt file + + language: String, programming language to output the strings as + + subs: String, sympy.Symbol, or list of Strings, or Bool + + substitutes the variable with the corrisponding value in the qgs model parameters + + if subs is True, all variables are substituted + + ''' + if equations is not None: + equation_dict = dict() + if subs is not None: + # make a dictionary of variables to substitute + sub_vals = dict() + if subs == True: + for v in params.symbol_to_value.values(): + sub_vals[v[0]] = v[1] + else: + + if isinstance(subs, list): + for s in subs: + if isinstance(s, str): + temp_sym, val = params.symbol_to_value[s] + elif isinstance(s, sy.Symbol): + temp_sym = s + val = params.symbol_to_value[temp_sym] + else: + raise ValueError("Incorrect type for substitution, needs to be string or sympy.Symbol, not: " + str(type(s))) + sub_vals[temp_sym] = val + else: + # assuming s is a sympy.Symbol or string + if isinstance(subs, str): + sub_vals[subs] = params.symbol_to_value[subs][1] + elif isinstance(subs, sy.Symbol): + val = params.symbol_to_value[subs] + sub_vals[subs] = val + else: + raise ValueError("Incorrect type for substitution, needs to be string or sympy.Symbol, not: " + str(type(subs))) + + + for k in equations.keys(): + eq = equations[k] + if subs is not None: + eq = eq.subs(sub_vals) + eq = eq.evalf() + + if (language is not None) and not(return_equations): + eq = translate_equations(eq, language) + + if save_loc is None: + equation_dict[k] = eq + else: + equation_dict[k] = str(eq) + + if return_equations: + return equation_dict + else: + if save_loc is None: + for eq in equation_dict.values(): + if save_loc is None: + print(eq) + else: + with open(save_loc, 'w') as f: + for eq in equation_dict.values(): + f.write("%s\n" % eq) + print("Equations written") + +def equation_as_function(equations, params, string_output=False, language='python', subs=None): + + vector_subs = dict() + if language == 'python': + for i in range(1, params.ndim+1): + vector_subs['U_'+str(i)] = sy.Symbol('U['+str(i-1)+']') + + if language == 'fortran': + for i in range(1, params.ndim+1): + vector_subs['U_'+str(i)] = sy.Symbol('U('+str(i)+')') + + if language == 'julia': + for i in range(1, params.ndim+1): + vector_subs['U_'+str(i)] = sy.Symbol('U['+str(i)+']') + + if language == 'mathematica': + for i in range(1, params.ndim+1): + vector_subs['U_'+str(i)] = sy.Symbol('U('+str(i)+')') + + + subed_eq = dict() + for k in equations.keys(): + subed_eq[k] = equations[k].subs(vector_subs) + + # determin which variables are still present in the equations + # vars = subed_eq.free_variables + + eq_list = print_equations(subed_eq, params, language=language, subs=subs, return_equations=True) + + if language == 'python': + if string_output: + # eq_list = translate_equations(eq_list, language='python') + + f_output = list() + f_output.append('def f(t, U, **kwargs):') + f_output.append('\t#Tendency function of the qgs model') + f_output.append('\tU_out = np.empty_like(U)') + for n, eq in enumerate(eq_list.values()): + f_output.append('\tU_out['+str(n)+'] = ' + str(eq)) + + f_output.append('\treturn U_out') + else: + # Return a lamdafied function + vec = [sy.Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] + f_output = sy.lambdify([vec], eq_list) + + if language == 'julia': + eq_list = translate_equations(eq_list, language='julia') + + f_output = list() + f_output.append('function f(t, U, kwargs...)') + f_output.append('\t#Tendency function of the qgs model') + f_output.append('\tU_out = similar(U)') + for n, eq in enumerate(eq_list.values()): + f_output.append('\tU_out['+str(n+1)+'] = ' + str(eq)) + + f_output.append('\treturn U_out') + f_output.append('end') + + #//TODO: Add statement for Fortran + #//TODO: Add statement for mathematica + + return f_output \ No newline at end of file diff --git a/qgs/params/params.py b/qgs/params/params.py index cd4dfca..4df2c4e 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -2074,6 +2074,7 @@ def _set_symbolic_parameters(self): """ #//TODO: THis function is a really lazy way of doing this, look into setting up the Parameter class with each symbol stored in the class + #//TODO: Need a better way to trigger this function, it cannot happen before the symbolic_qgtensor class is initiated I think? self.symbol_to_value = dict() # Scale Parameters diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 70cac13..0db4863 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -724,6 +724,8 @@ def compute_tensor(self): sy_arr_dic = self._compute_tensor_dicts() sy_arr_dic = self.remove_dic_zeros(sy_arr_dic) + self.params._set_symbolic_parameters() + if sy_arr_dic is not None: self._set_tensor(sy_arr_dic) @@ -812,8 +814,6 @@ def subs_tensor(self, tensor=None, dict_opp=True): """ Uses sympy substitution to convert the symbolic tensor or a symbolic dictionary to a numerical one. """ - - self.params._set_symbolic_parameters() symbol_to_number_map = list() From fed0c4730742a6fcaeed4aeb98f6af26e61f0531 Mon Sep 17 00:00:00 2001 From: ushham Date: Sat, 5 Aug 2023 16:47:37 +0200 Subject: [PATCH 032/143] addition of T4 tensor calc --- qgs/tensors/symbolic_qgtensor.py | 229 +++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 0db4863..9a9a586 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -1281,6 +1281,235 @@ def compute_tensor(self): if symbolic_dict_dynT is not None: self._set_tensor(symbolic_dict_dynT) +class SymbolicTensorT4(SymbolicTensorLinear): + #//TODO: Need to work out symbolic tensor dot + + """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. + + Parameters + ---------- + params: None or QgParams, optional + The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. + atmospheric_inner_products: None or AtmosphericInnerProducts, optional + The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. + If None, disable the atmospheric tendencies. Default to `None`. + oceanic_inner_products: None or OceanicInnerProducts, optional + The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. + If None, disable the oceanic tendencies. Default to `None`. + ground_inner_products: None or GroundInnerProducts, optional + The inner products of the ground basis functions on which the model's PDE ground equations are projected. + If None, disable the ground tendencies. Default to `None`. + + Attributes + ---------- + params: None or QgParams + The models parameters used to configure the tensor. `None` for an empty tensor. + atmospheric_inner_products: None or AtmosphericInnerProducts + The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. + If None, disable the atmospheric tendencies. Default to `None`. + oceanic_inner_products: None or OceanicInnerProducts + The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. + If None, disable the oceanic tendencies. Default to `None`. + ground_inner_products: None or GroundInnerProducts + The inner products of the ground basis functions on which the model's PDE ground equations are projected. + If None, disable the ground tendencies. Default to `None`. + tensor: sparse.COO(float) + The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. + jacobian_tensor: sparse.COO(float) + The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. + """ + + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): + + SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) + + if params.dynamic_T: + self.compute_tensor() + + def _compute_tensor_dicts(self): + + if self.params is None: + return None + + if self.atmospheric_inner_products is None and self.oceanic_inner_products is None \ + and self.ground_inner_products is None: + return None + + aips = self.atmospheric_inner_products + + bips = None + if self.oceanic_inner_products is not None: + bips = self.oceanic_inner_products + + elif self.ground_inner_products is not None: + bips = self.ground_inner_products + + if bips is not None: + go = bips.stored + else: + go = True + + symbolic_array_full_dict = self._compute_non_stored_full_dict() + + return symbolic_array_full_dict + + def _compute_non_stored_full_dict(self): + par = self.params + nvar = par.number_of_variables + aips = self.atmospheric_inner_products + + bips = None + if self.oceanic_inner_products is not None: + bips = self.oceanic_inner_products + ocean = True + else: + ocean = False + + if self.ground_inner_products is not None: + bips = self.ground_inner_products + ground_temp = True + else: + ground_temp = False + + # constructing some derived matrices + if aips is not None: + a_theta = dict() + a_theta = np.zeros((nvar[1], nvar[1])) + for i in range(nvar[1]): + for j in range(nvar[1]): + a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) + + a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) + a_theta = a_theta.inverse() + a_theta = a_theta.simplify() + + if bips is not None: + if ocean: + U_inv = dict() + for i in range(nvar[3]): + for j in range(nvar[3]): + U_inv[i, j] = bips.U(i, j) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) + + else: + U_inv = dict() + for i in range(nvar[2]): + for j in range(nvar[2]): + U_inv[(i, j)] = bips.U(i, j) + U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) + + U_inv = U_inv.inverse() + U_inv = U_inv.simplify() + + + ################# + + sy_arr_dic = dict() + # theta_a part + for i in range(nvar[1]): + + if self.T4LSBpa is not None: + for j in range(nvar[1]): + for k in range(nvar[1]): + for ell in range(nvar[1]): + for m in range(nvar[1]): + val = 0 + for jj in range(nvar[1]): + val += a_theta[i, jj] * aips.z(jj, j, k, ell, m) + + sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4LSBpa * val + + if ocean: + if self.T4LSBpgo is not None: + for j in range(nvar[3]): + for k in range(nvar[3]): + for ell in range(nvar[3]): + for m in range(nvar[3]): + val = 0 + for jj in range(nvar[1]): + val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) + + sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4LSBpgo * val + + if ground_temp: + if self.T4LSBpgo is not None: + for j in range(nvar[2]): + for k in range(nvar[2]): + for ell in range(nvar[2]): + for m in range(nvar[2]): + val = 0 + for jj in range(nvar[1]): + val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) + + sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4LSBpgo * val + + if ocean: + + # deltaT_o part + for i in range(nvar[3]): + for j in range(nvar[1]): + for k in range(nvar[1]): + for ell in range(nvar[1]): + for m in range(nvar[1]): + val = 0 + for jj in range(nvar[3]): + val += U_inv[i, jj] * bips.Z(jj, j, k, ell, m) + + sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + + for j in range(nvar[3]): + for k in range(nvar[3]): + for ell in range(nvar[3]): + for m in range(nvar[3]): + val = 0 + for jj in range(nvar[3]): + val -= U_inv[i, jj] * bips.V(jj, j, k, ell, m) + + sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4sbpgo * val + + # deltaT_g part + if ground_temp: + for i in range(nvar[2]): + for j in range(nvar[1]): + for k in range(nvar[1]): + for ell in range(nvar[1]): + for m in range(nvar[1]): + val = 0 + for jj in range(nvar[2]): + val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] + + sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + + for j in range(nvar[2]): + for k in range(nvar[2]): + for ell in range(nvar[2]): + for m in range(nvar[2]): + val = 0 + for jj in range(nvar[2]): + val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] + + sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4sbpgo * val + + return sy_arr_dic + + def compute_tensor(self): + """Routine to compute the tensor.""" + # gathering + if self.params.T4: + #//TODO: Make a proper error message for here + raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") + + symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) + symbolic_dict_linear = _shift_dict_keys(symbolic_dict_linear, (0, 0)) + + symbolic_dict_dynT = self._compute_tensor_dicts() + + if symbolic_dict_linear is not None: + symbolic_dict_dynT = {**symbolic_dict_linear, **symbolic_dict_dynT} + + if symbolic_dict_dynT is not None: + self._set_tensor(symbolic_dict_dynT) + def _kronecker_delta(i, j): From 45289e79f50dacaf6e0e2288a9a1f6daec115ccc Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 7 Aug 2023 15:20:14 +0200 Subject: [PATCH 033/143] Fixed non-stored versions --- qgs/functions/symbolic_output.py | 4 +-- qgs/inner_products/symbolic.py | 44 +++++++++---------------- qgs/tensors/symbolic_qgtensor.py | 56 ++++++-------------------------- 3 files changed, 27 insertions(+), 77 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index bfe4d1d..8d8a932 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -5,7 +5,7 @@ from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, GroundSymbolicInnerProducts -from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT +from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT, SymbolicTensorT4 python_lang_translation = { 'sqrt': 'math.sqrt', @@ -60,7 +60,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, sim aip.connect_to_ground(gip) if params.T4: - raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") + agotensor = SymbolicTensorT4(params, aip, oip, gip) elif params.dynamic_T: agotensor = SymbolicTensorDynamicT(params, aip, oip, gip) else: diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index f176c6e..8da2d6a 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -496,6 +496,10 @@ def natm(self): # !-----------------------------------------------------! def _integrate(self, subs, args): + if self.return_symbolic: + res = _apply(args) + return res[1] + if self.quadrature: res = _num_apply(args) return res[1] @@ -1004,6 +1008,10 @@ def noc(self): # !-----------------------------------------------------! def _integrate(self, subs, args): + if self.return_symbolic: + res = _apply(args) + return res[1] + if self.quadrature: res = _num_apply(args) return res[1] @@ -1376,6 +1384,10 @@ def ngr(self): # !-----------------------------------------------------! def _integrate(self, subs, args): + if self.return_symbolic: + res = _apply(args) + return res[1] + if self.quadrature: res = _num_apply(args) return res[1] @@ -1480,6 +1492,7 @@ def _num_apply(ls): else: num_integrand = integrand[0] + # TODO: sy.integrate.ddquad says that the function needs to be f(y, x), not f(x, y)? func = sy.lambdify((integrand[1][0], integrand[2][0]), num_integrand, 'numpy') try: @@ -1569,11 +1582,9 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False): result_list = dict() - - #//TODO: I am not sure what this timeout does, I have switched if off as it was leading to non-convergence. + #//TODO: This function needs checking, I have stripped alot of the funcitionality out of _parallell_compute future = pool.map(_apply, args_list, timeout=None) results = future.result() - num_args_list = list() i = 0 while True: try: @@ -1595,32 +1606,7 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False result_list[tuple(idx)] = res[1] except StopIteration: - break - except TimeoutError: - num_args_list.append(args_list[i]) - i += 1 - - future = pool.map(_num_apply, num_args_list) - results = future.result() - - - while True: - try: - res = next(results) - result_list[res[0]] = res[1] - - if permute: - res = next(results) - i = res[0][0] - idx = res[0][1:] - perm_idx = multiset_permutations(idx) - for perm in perm_idx: - idx = [i] + perm - result_list[tuple(idx)] = res[1] - - except StopIteration: - break - + break return result_list if __name__ == '__main__': diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 9a9a586..4da9da8 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -401,7 +401,7 @@ def _compute_tensor_dicts(self): if gp is not None: if symbolic_params['hk'] is not None: - #//TODO: Need to make this symbolic + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -self.sig0 * oro[i, j][0] / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro[i, j][0] / 2) @@ -515,7 +515,6 @@ def _compute_tensor_dicts(self): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) - #//TODO: what is gp.hk parameter??? if gp is not None: if symbolic_params['hk'] is not None: oro = 0 @@ -919,7 +918,6 @@ def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): print(str(ix) + ": " + str(output_val)) class SymbolicTensorDynamicT(SymbolicTensorLinear): - #//TODO: Need to work out symbolic tensor dot """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. @@ -1145,14 +1143,12 @@ def _compute_non_stored_full_dict(self): # constructing some derived matrices if aips is not None: a_theta = dict() - a_theta = np.zeros((nvar[1], nvar[1])) for i in range(nvar[1]): for j in range(nvar[1]): a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() - a_theta = a_theta.simplify() if bips is not None: if ocean: @@ -1170,7 +1166,6 @@ def _compute_non_stored_full_dict(self): U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) U_inv = U_inv.inverse() - U_inv = U_inv.simplify() ################# @@ -1268,7 +1263,7 @@ def compute_tensor(self): # gathering if self.params.T4: #//TODO: Make a proper error message for here - raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") + raise ValueError("Parameters are set for T4 version, set dynamic_T=True") symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) symbolic_dict_linear = _shift_dict_keys(symbolic_dict_linear, (0, 0)) @@ -1282,7 +1277,6 @@ def compute_tensor(self): self._set_tensor(symbolic_dict_dynT) class SymbolicTensorT4(SymbolicTensorLinear): - #//TODO: Need to work out symbolic tensor dot """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. @@ -1323,36 +1317,9 @@ def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_p SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) - if params.dynamic_T: + if params.T4: self.compute_tensor() - def _compute_tensor_dicts(self): - - if self.params is None: - return None - - if self.atmospheric_inner_products is None and self.oceanic_inner_products is None \ - and self.ground_inner_products is None: - return None - - aips = self.atmospheric_inner_products - - bips = None - if self.oceanic_inner_products is not None: - bips = self.oceanic_inner_products - - elif self.ground_inner_products is not None: - bips = self.ground_inner_products - - if bips is not None: - go = bips.stored - else: - go = True - - symbolic_array_full_dict = self._compute_non_stored_full_dict() - - return symbolic_array_full_dict - def _compute_non_stored_full_dict(self): par = self.params nvar = par.number_of_variables @@ -1374,14 +1341,12 @@ def _compute_non_stored_full_dict(self): # constructing some derived matrices if aips is not None: a_theta = dict() - a_theta = np.zeros((nvar[1], nvar[1])) for i in range(nvar[1]): for j in range(nvar[1]): a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() - a_theta = a_theta.simplify() if bips is not None: if ocean: @@ -1399,7 +1364,6 @@ def _compute_non_stored_full_dict(self): U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) U_inv = U_inv.inverse() - U_inv = U_inv.simplify() ################# @@ -1440,7 +1404,7 @@ def _compute_non_stored_full_dict(self): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) - + sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4LSBpgo * val if ocean: @@ -1495,20 +1459,20 @@ def _compute_non_stored_full_dict(self): def compute_tensor(self): """Routine to compute the tensor.""" # gathering - if self.params.T4: + if not(self.params.T4): #//TODO: Make a proper error message for here - raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") + raise ValueError("Parameters are not set for T4 version") symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) symbolic_dict_linear = _shift_dict_keys(symbolic_dict_linear, (0, 0)) - symbolic_dict_dynT = self._compute_tensor_dicts() + symbolic_dict_T4 = self._compute_non_stored_full_dict() if symbolic_dict_linear is not None: - symbolic_dict_dynT = {**symbolic_dict_linear, **symbolic_dict_dynT} + symbolic_dict_T4 = {**symbolic_dict_linear, **symbolic_dict_T4} - if symbolic_dict_dynT is not None: - self._set_tensor(symbolic_dict_dynT) + if symbolic_dict_T4 is not None: + self._set_tensor(symbolic_dict_T4) def _kronecker_delta(i, j): From dbf03ba2cedd74197ad1883fd5e7f9055d1f3299 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 8 Aug 2023 15:23:03 +0200 Subject: [PATCH 034/143] Bugs fixed in numerically testing --- qgs/functions/symbolic_output.py | 11 ++++---- qgs/inner_products/symbolic.py | 9 +++--- qgs/tensors/symbolic_qgtensor.py | 48 +++++++++++++++++++++----------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 8d8a932..d0572fd 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -25,7 +25,7 @@ '**': '^' } -def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, simplify=False, return_inner_products=False, return_jacobian=False, return_symbolic_dict=False, return_symbolic_qgtensor=False): +def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, simplify=False, return_inner_products=False, return_jacobian=False, return_symbolic_dict=False, return_symbolic_qgtensor=False, numerically_test_tensor=True): """ Function to output the raw symbolic functions of the qgs model. """ @@ -60,11 +60,11 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, sim aip.connect_to_ground(gip) if params.T4: - agotensor = SymbolicTensorT4(params, aip, oip, gip) + agotensor = SymbolicTensorT4(params, aip, oip, gip, numerically_test_tensor) elif params.dynamic_T: - agotensor = SymbolicTensorDynamicT(params, aip, oip, gip) + agotensor = SymbolicTensorDynamicT(params, aip, oip, gip, numerically_test_tensor) else: - agotensor = SymbolicTensorLinear(params, aip, oip, gip) + agotensor = SymbolicTensorLinear(params, aip, oip, gip, numerically_test_tensor) xx = list() xx.append(1) @@ -85,8 +85,9 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, sim eq_simplified = dict() Deq_simplified = dict() - #//TODO: This section using .simplify() is super slow. if simplify: + # Simplifying at this step is slow + # This only needs to be used if no substitutions are being made for i in range(1, params.ndim+1): eq_simplified[i] = eq[i].simplify() if return_jacobian: diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 8da2d6a..5c4ed52 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -1589,10 +1589,11 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False while True: try: res = next(results) + expr = res[1].simplify() if subs is not None: - result_list[res[0]] = res[1].subs(subs) + result_list[res[0]] = expr.subs(subs) else: - result_list[res[0]] = res[1] + result_list[res[0]] = expr if permute: i = res[0][0] @@ -1601,9 +1602,9 @@ def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False for perm in perm_idx: idx = [i] + perm if subs is not None: - result_list[tuple(idx)] = res[1].subs(subs) + result_list[tuple(idx)] = expr.subs(subs) else: - result_list[tuple(idx)] = res[1] + result_list[tuple(idx)] = expr except StopIteration: break diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 4da9da8..b5e1533 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -13,6 +13,7 @@ import numpy as np import sparse as sp import sympy as sy +import warnings import pickle #//TODO: Check non stored IP version of this @@ -56,7 +57,7 @@ class SymbolicTensorLinear(object): The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. """ - def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None, numerically_test_tensor=True): self.atmospheric_inner_products = atmospheric_inner_products self.oceanic_inner_products = oceanic_inner_products @@ -68,9 +69,13 @@ def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_p self.tensor = None self.jacobian_tensor = None + self.test_tensor = numerically_test_tensor + self.params.symbolic_insolation_array() self.params.symbolic_orography_array() + self.params._set_symbolic_parameters() + if not(self.params.dynamic_T): self.compute_tensor() @@ -723,8 +728,6 @@ def compute_tensor(self): sy_arr_dic = self._compute_tensor_dicts() sy_arr_dic = self.remove_dic_zeros(sy_arr_dic) - self.params._set_symbolic_parameters() - if sy_arr_dic is not None: self._set_tensor(sy_arr_dic) @@ -734,7 +737,11 @@ def _set_tensor(self, dic, set_symbolic=False): if set_symbolic: self._set_symbolic_tensor() - + + if self.test_tensor: + print("Testing Tensor Numerically") + self.test_tensor_numerically(self.tensor_dic) + def _set_symbolic_tensor(self): ndim = self.params.ndim @@ -848,10 +855,10 @@ def subs_tensor(self, tensor=None, dict_opp=True): return ten_out - def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): + def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-14): """ Uses sympy substitution to convert the symbolic tensor, or symbolic dictionary, to a numerical one. - This is then compared to the tensor calculated in the qgs.tensor.symbolic module. + This is then compared to the tensor calculated by the qgs.tensor.symbolic module. """ ndim = self.params.ndim @@ -874,9 +881,10 @@ def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): tensor = self.tensor subbed_ten = self.subs_tensor(tensor) - #//TODO: Clean up this messy COO if isinstance(subbed_ten, dict): - subbed_tensor_sp = sp.COO(np.array([list(k) for k in subbed_ten.keys()]).T, np.array(list(subbed_ten.values()), dtype=float), shape=dims) + coords = np.array([list(k) for k in subbed_ten.keys()]).T + data = np.array(list(subbed_ten.values()), dtype=float) + subbed_tensor_sp = sp.COO(coords, data, shape=dims) else: subbed_ten = np.array(subbed_ten) subbed_tensor_np = np.array(subbed_ten).astype(np.float64) @@ -884,11 +892,19 @@ def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): diff_arr = subbed_tensor_sp.todense() - numerical_tensor.tensor.todense() + #TODO: Is there a better way to do error/pass messages? total_error = np.sum(np.abs(diff_arr)) - if total_error > tol: - self.print_tensor(diff_arr, tol) + max_error = np.max(np.abs(diff_arr)) + + if max_error > tol: + self.print_tensor(diff_arr, tol=tol) raise ValueError("Symbolic tensor and numerical tensor do not match at the above coordinates, with a total error of: " + str(total_error)) + + elif total_error > tol: + warnings.warn("Symbolic tensor and numerical tensor have a combined error of: " + str(total_error)) + else: + print("Tensor passes numerical test with a combined error of less than: " + str(tol)) def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): if tensor is None: @@ -954,9 +970,9 @@ class SymbolicTensorDynamicT(SymbolicTensorLinear): The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. """ - def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None, numerically_test_tensor=True): - SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) + SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products, numerically_test_tensor) if params.dynamic_T: self.compute_tensor() @@ -1272,12 +1288,12 @@ def compute_tensor(self): if symbolic_dict_linear is not None: symbolic_dict_dynT = {**symbolic_dict_linear, **symbolic_dict_dynT} - + if symbolic_dict_dynT is not None: self._set_tensor(symbolic_dict_dynT) class SymbolicTensorT4(SymbolicTensorLinear): - + # TODO: this takes a long time (>1hr) to run. I think we need a better way to run the non-stored z, v, Z, V IPs. """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. Parameters @@ -1313,9 +1329,9 @@ class SymbolicTensorT4(SymbolicTensorLinear): The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. """ - def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None, numerically_test_tensor=True): - SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) + SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products, numerically_test_tensor) if params.T4: self.compute_tensor() From 7364af242571ea59a68c8e84126b2f727ee7a82a Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 8 Aug 2023 16:17:18 +0200 Subject: [PATCH 035/143] Finding error in DE version --- notebooks/Symbolic Output-Linear.ipynb | 207 ++++++++++++++--- notebooks/Symbolic Output-dynamic_T.ipynb | 262 ++++++++++++++++------ qgs/tensors/symbolic_qgtensor.py | 5 +- 3 files changed, 372 insertions(+), 102 deletions(-) diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index a347c74..407f178 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -91,9 +91,8 @@ "outputs": [], "source": [ "%%time\n", - "# atm_ip = AtmosphericSymbolicInnerProducts(model_parameters)\n", "# Takes ~5 mins\n", - "ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", + "# ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", "ocn_ip = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, @@ -108,7 +107,7 @@ "source": [ "%%time\n", "# Takes ~5 mins\n", - "atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", + "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, @@ -119,8 +118,8 @@ "metadata": {}, "outputs": [], "source": [ - "atm_ip_stored.save_to_file('temp_atm_sym_lin.ip')\n", - "ocn_ip_stored.save_to_file('temp_ocn_sym_lin.ip')" + "# atm_ip_stored.save_to_file('temp_atm_sym_lin.ip')\n", + "# ocn_ip_stored.save_to_file('temp_ocn_sym_lin.ip')" ] }, { @@ -158,102 +157,254 @@ { "cell_type": "code", "execution_count": null, - "id": "d964e8f2", + "id": "f67ffc01", "metadata": {}, "outputs": [], "source": [ - "model_parameters.symbolic_params['go_C']" + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, print_equations, equation_as_function" ] }, { "cell_type": "code", "execution_count": null, - "id": "e6cb2cda", - "metadata": {}, + "id": "9479fcb0", + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "model_parameters.number_of_variables" + "%%time\n", + "funcs, = create_symbolic_equations(model_parameters, atm_loaded, ocn_loaded)" ] }, { "cell_type": "code", "execution_count": null, - "id": "19d0dd71", - "metadata": {}, + "id": "b91f057b", + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "ocn_loaded._O.shape" + "ten = SymbolicTensorLinear(model_parameters, atm_loaded, ocn_loaded)" ] }, { "cell_type": "code", "execution_count": null, - "id": "2e17b891", + "id": "2b9c80ec", "metadata": {}, "outputs": [], "source": [ - "ten = SymbolicTensorLinear(params=model_parameters, atmospheric_inner_products=atm_loaded, oceanic_inner_products=ocn_loaded)" + "ten.print_tensor(ocn_loaded._C)" ] }, { "cell_type": "code", "execution_count": null, - "id": "e2037a75", + "id": "870fe100", "metadata": {}, "outputs": [], "source": [ - "ten.params.dynamic_T" + "%%time\n", + "fx = equation_as_function(funcs, model_parameters, subs=True, language='python', string_output=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "cf668cb9", + "id": "c385c4be", "metadata": { - "scrolled": false + "scrolled": true }, "outputs": [], "source": [ - "%%time\n", - "ten.compute_tensor()" + "for i in fx:\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ec572eb", + "metadata": {}, + "outputs": [], + "source": [ + "from numba import njit" ] }, { "cell_type": "code", "execution_count": null, - "id": "8ed132e4", + "id": "d5d98d6b", + "metadata": {}, + "outputs": [], + "source": [ + "@njit\n", + "def f(t, U):\n", + "\t#Tendency function of the qgs model\n", + "\tU_out = np.empty_like(U)\n", + "\tU_out[0] = -0.05*U[0] + 0.05*U[10] + 0.168986428105931*U[21] + 0.251005505082615*U[23]\n", + "\tU_out[1] = -0.980418808722262*U[0]*U[2] - 0.980418808722262*U[10]*U[12] + 0.05*U[11] - 1.56867009395562*U[13]*U[15] - 1.50055762081784*U[14]*U[17] + 1.50055762081784*U[15]*U[16] - 0.05*U[1] - 0.0112217054051163*U[20] + 0.120745727252785*U[2] - 1.56867009395562*U[3]*U[5] - 1.50055762081784*U[4]*U[7] + 1.50055762081784*U[5]*U[6]\n", + "\tU_out[2] = 0.980418808722262*U[0]*U[1] + 0.980418808722262*U[10]*U[11] + 0.05*U[12] + 1.56867009395562*U[13]*U[14] + 1.50055762081784*U[14]*U[16] + 1.50055762081784*U[15]*U[17] - 0.120745727252785*U[1] + 0.05*U[24] - 0.05*U[2] + 1.56867009395562*U[3]*U[4] + 1.50055762081784*U[4]*U[6] + 1.50055762081784*U[5]*U[7]\n", + "\tU_out[3] = 1.87265793760678*U[11]*U[15] - 1.87265793760678*U[12]*U[14] + 0.05*U[13] + 3.74531587521356*U[16]*U[19] - 3.74531587521356*U[17]*U[18] + 1.87265793760678*U[1]*U[5] - 0.00679432430697251*U[20] + 0.0810088839426413*U[22] - 1.87265793760678*U[2]*U[4] - 0.05*U[3] + 3.74531587521356*U[6]*U[9] - 3.74531587521356*U[7]*U[8]\n", + "\tU_out[4] = -1.02902937637678*U[0]*U[5] - 1.02902937637678*U[10]*U[15] + 1.73752196836555*U[11]*U[17] + 0.574852231579351*U[12]*U[13] - 1.73752196836555*U[12]*U[16] + 0.05*U[14] + 1.73752196836555*U[1]*U[7] - 0.0164935614721478*U[21] + 0.574852231579351*U[2]*U[3] - 1.73752196836555*U[2]*U[6] - 0.05*U[4] + 0.0570836566449895*U[5]\n", + "\tU_out[5] = 1.02902937637678*U[0]*U[4] + 1.02902937637678*U[10]*U[14] - 0.574852231579351*U[11]*U[13] - 1.73752196836555*U[11]*U[16] - 1.73752196836555*U[12]*U[17] + 0.05*U[15] - 0.574852231579351*U[1]*U[3] - 1.73752196836555*U[1]*U[6] + 0.05*U[25] - 1.73752196836555*U[2]*U[7] - 0.0570836566449895*U[4] - 0.05*U[5]\n", + "\tU_out[6] = -2.71889339738442*U[0]*U[7] - 2.71889339738442*U[10]*U[17] + 0.753865979381443*U[11]*U[15] + 0.753865979381443*U[12]*U[14] - 4.35022943581506*U[13]*U[19] + 0.05*U[16] + 0.753865979381443*U[1]*U[5] - 0.000777999678859866*U[20] + 0.753865979381443*U[2]*U[4] - 4.35022943581506*U[3]*U[9] - 0.05*U[6] + 0.0837128882242243*U[7]\n", + "\tU_out[7] = 2.71889339738442*U[0]*U[6] + 2.71889339738442*U[10]*U[16] - 0.753865979381443*U[11]*U[14] + 0.753865979381443*U[12]*U[15] + 4.35022943581506*U[13]*U[18] + 0.05*U[17] - 0.753865979381443*U[1]*U[4] + 0.753865979381443*U[2]*U[5] + 4.35022943581506*U[3]*U[8] - 0.0837128882242243*U[6] - 0.05*U[7]\n", + "\tU_out[8] = -2.26482546109569*U[0]*U[9] - 2.26482546109569*U[10]*U[19] - 1.7450294536311*U[13]*U[17] + 0.05*U[18] - 0.0017443933973331*U[21] - 1.7450294536311*U[3]*U[7] - 0.05*U[8] + 0.0603728636263923*U[9]\n", + "\tU_out[9] = 2.26482546109569*U[0]*U[8] + 2.26482546109569*U[10]*U[18] + 1.7450294536311*U[13]*U[16] + 0.05*U[19] + 1.7450294536311*U[3]*U[6] - 0.0603728636263923*U[8] - 0.05*U[9]\n", + "\tU_out[10] = 0.00454545454545455*U[0] - 0.0299589840451022*U[10] - 1.41868025576271*U[11]*U[2] + 1.41868025576271*U[12]*U[1] - 1.13494420461017*U[14]*U[5] + 1.13494420461017*U[15]*U[4] - 2.83736051152543*U[16]*U[7] + 2.83736051152543*U[17]*U[6] - 2.26988840922034*U[18]*U[9] + 2.26988840922034*U[19]*U[8] - 0.0153624025550847*U[21] - 0.0228186822802377*U[23] + 0.00807504401792934*U[29] + 0.00323001760717173*U[31] + 0.000468670500616653\n", + "\tU_out[11] = -1.43757363347933*U[0]*U[12] + 1.02191932371371*U[10]*U[2] - 0.0352914755316095*U[11] + 0.0255954299692664*U[12] + 1.63507091794193*U[13]*U[5] + 1.21855791962175*U[14]*U[7] - 2.30011781356693*U[15]*U[3] - 1.21855791962175*U[15]*U[6] + 1.85472813238771*U[16]*U[5] - 1.85472813238771*U[17]*U[4] + 0.0105988967691095*U[1] + 0.00237875394324372*U[20] - 0.00388732494300134*U[28]\n", + "\tU_out[12] = 1.43757363347933*U[0]*U[11] - 1.02191932371371*U[10]*U[1] - 0.0255954299692664*U[11] - 0.0352914755316095*U[12] - 1.63507091794193*U[13]*U[4] + 2.30011781356693*U[14]*U[3] - 1.21855791962175*U[14]*U[6] - 1.21855791962175*U[15]*U[7] + 1.85472813238771*U[16]*U[4] + 1.85472813238771*U[17]*U[5] - 0.0105988967691095*U[24] + 0.0105988967691095*U[2] + 0.00915929361228703*U[32]\n", + "\tU_out[13] = -1.24843862507119*U[11]*U[5] + 1.24843862507119*U[12]*U[4] - 0.0385392017497231*U[13] - 2.3185288751322*U[14]*U[2] + 2.3185288751322*U[15]*U[1] - 2.49687725014237*U[16]*U[9] + 2.49687725014237*U[17]*U[8] - 4.63705775026441*U[18]*U[7] + 4.63705775026441*U[19]*U[6] + 0.00194123551627786*U[20] - 0.0231453954121832*U[22] - 0.00317233872132938*U[28] + 0.00571020969839289*U[30] + 0.0142857142857143*U[3]\n", + "\tU_out[14] = -1.16886956037576*U[0]*U[15] + 0.422511733532696*U[10]*U[5] - 0.612715105162524*U[11]*U[7] - 1.38291034440645*U[12]*U[3] + 0.612715105162524*U[12]*U[6] + 1.79985224341047*U[13]*U[2] - 0.0419279046842654*U[14] + 0.0207014663040147*U[15] - 1.87294455066922*U[16]*U[2] + 1.87294455066922*U[17]*U[1] + 0.00598141266899433*U[21] - 0.00314405057531466*U[29] + 0.0181325685149777*U[4]\n", + "\tU_out[15] = 1.16886956037576*U[0]*U[14] - 0.422511733532696*U[10]*U[4] + 1.38291034440645*U[11]*U[3] + 0.612715105162524*U[11]*U[6] + 0.612715105162524*U[12]*U[7] - 1.79985224341047*U[13]*U[1] - 0.0207014663040147*U[14] - 0.0419279046842654*U[15] - 1.87294455066922*U[16]*U[1] - 1.87294455066922*U[17]*U[2] - 0.0181325685149777*U[25] + 0.00740799464244248*U[33] + 0.0181325685149777*U[5]\n", + "\tU_out[16] = -2.94535914360826*U[0]*U[17] + 0.569389237785845*U[10]*U[7] - 0.768581081081081*U[11]*U[5] - 0.768581081081081*U[12]*U[4] + 0.911022780457352*U[13]*U[9] + 1.42736486486486*U[14]*U[2] + 1.42736486486486*U[15]*U[1] - 0.0451998211991061*U[16] + 0.0365772529628368*U[17] - 4.71257462977322*U[19]*U[3] + 0.000339936796618951*U[20] - 0.000555519746922151*U[28] + 0.0218468468468468*U[6]\n", + "\tU_out[17] = 2.94535914360826*U[0]*U[16] - 0.569389237785845*U[10]*U[6] + 0.768581081081081*U[11]*U[4] - 0.768581081081081*U[12]*U[5] - 0.911022780457352*U[13]*U[8] - 1.42736486486486*U[14]*U[1] + 1.42736486486486*U[15]*U[2] - 0.0365772529628368*U[16] - 0.0451998211991061*U[17] + 4.71257462977322*U[18]*U[3] + 0.0218468468468468*U[7]\n", + "\tU_out[18] = -2.37660377951895*U[0]*U[19] + 0.0288656329496225*U[10]*U[9] + 1.50101291338039*U[13]*U[7] - 3.30992591155675*U[17]*U[3] - 0.0487836620662873*U[18] + 0.031291522765895*U[19] + 0.000904126828290182*U[21] - 0.000475242326846696*U[29] + 0.0259152215799615*U[8]\n", + "\tU_out[19] = 2.37660377951895*U[0]*U[18] - 0.0288656329496225*U[10]*U[8] - 1.50101291338039*U[13]*U[6] + 3.30992591155675*U[16]*U[3] - 0.031291522765895*U[18] - 0.0487836620662873*U[19] + 0.0259152215799615*U[9]\n", + "\tU_out[20] = 6.35094981132412e-8*U[11] + 8.50239961075093e-8*U[13] + 3.66419111790894e-8*U[16] - 6.35094981132412e-8*U[1] - 1.58263150747531e-7*U[20] - 0.000969739516006511*U[21]*U[24] - 0.00116937805032627*U[21]*U[26] - 0.00278561057700379*U[22]*U[25] - 0.00308506837848344*U[22]*U[27] - 0.00534779391583196*U[23]*U[26] - 7.91389255433123e-5*U[24] - 8.50239961075093e-8*U[3] - 3.66419111790894e-8*U[6]\n", + "\tU_out[21] = 4.2438907259419e-8*U[0] - 4.2438907259419e-8*U[10] + 1.34106967724862e-7*U[14] + 5.07202450868017e-8*U[18] - 0.000708243330589963*U[20]*U[24] + 0.00172613939940631*U[20]*U[26] - 4.91188341087484e-7*U[21] - 0.00626988589506499*U[22]*U[24] - 0.0119940379417069*U[23]*U[25] - 7.9002862144845e-5*U[25] - 1.34106967724862e-7*U[4] - 5.07202450868017e-8*U[8]\n", + "\tU_out[22] = -1.52343526260616e-7*U[13] - 0.00317032731211854*U[20]*U[25] + 0.0060425658523595*U[20]*U[27] + 0.00160884360522711*U[21]*U[24] - 1.04352693785129e-6*U[22] - 0.0178532887343688*U[23]*U[24] - 7.87771265478973e-5*U[26] + 1.52343526260616e-7*U[3]\n", + "\tU_out[23] = 1.68596160851496e-8*U[0] - 1.68596160851496e-8*U[10] - 0.00857174744319731*U[20]*U[26] - 0.00140681174654471*U[21]*U[25] + 0.00871788567853858*U[22]*U[24] - 1.8115193420227e-6*U[23] - 7.84632552652771e-5*U[27]\n", + "\tU_out[24] = -1.49531921957748e-7*U[12] + 0.00167798163428125*U[20]*U[21] + 7.9081381622411e-5*U[20] + 0.0046610600952257*U[21]*U[22] + 0.00913567778664237*U[22]*U[23] - 2.99063843915495e-7*U[24] + 1.49531921957748e-7*U[2]\n", + "\tU_out[25] = -3.15752734584711e-7*U[15] + 0.0059559067658186*U[20]*U[22] + 0.0134007902230919*U[21]*U[23] + 7.89455158524528e-5*U[21] - 6.31505469169422e-7*U[25] + 3.15752734584711e-7*U[5]\n", + "\tU_out[26] = -0.0005567719890759*U[20]*U[21] + 0.0139192997268975*U[20]*U[23] + 7.8720107381223e-5*U[22] - 1.18304364206348e-6*U[26]\n", + "\tU_out[27] = -0.00295762797162433*U[20]*U[22] + 7.84066893926385e-5*U[23] - 1.94992690884148e-6*U[27]\n", + "\tU_out[28] = -0.000962032794821447*U[11] - 0.00086613382185597*U[13] - 0.000192406558964289*U[16] + 0.975*U[21]*U[32] - 0.325*U[21]*U[34] + 1.3*U[22]*U[33] - 0.65*U[22]*U[35] + 1.625*U[23]*U[34] - 0.975*U[24]*U[29] - 1.3*U[25]*U[30] + 0.325*U[26]*U[29] - 1.625*U[26]*U[31] + 0.65*U[27]*U[30] - 0.00122336344718992*U[28]\n", + "\tU_out[29] = 0.00173226764371194*U[10] - 0.000962032794821447*U[14] - 0.000192406558964289*U[18] - 0.975*U[20]*U[32] + 0.325*U[20]*U[34] + 1.625*U[22]*U[32] + 1.95*U[23]*U[33] + 0.975*U[24]*U[28] - 1.625*U[24]*U[30] - 1.95*U[25]*U[31] - 0.325*U[26]*U[28] - 0.00122336344718992*U[29] + 0.000137893034416115\n", + "\tU_out[30] = 0.00155904087934075*U[13] - 1.3*U[20]*U[33] + 0.65*U[20]*U[35] - 1.625*U[21]*U[32] + 2.275*U[23]*U[32] + 1.625*U[24]*U[29] - 2.275*U[24]*U[31] + 1.3*U[25]*U[28] - 0.65*U[27]*U[28] - 0.00122336344718992*U[30]\n", + "\tU_out[31] = 0.000692907057484776*U[10] - 1.625*U[20]*U[34] - 1.95*U[21]*U[33] - 2.275*U[22]*U[32] + 2.275*U[24]*U[30] + 1.95*U[25]*U[29] + 1.625*U[26]*U[28] - 0.00122336344718992*U[31] + 5.51572137664459e-5\n", + "\tU_out[32] = 0.00226673637054264*U[12] + 0.975*U[20]*U[29] - 0.975*U[21]*U[28] + 1.625*U[21]*U[30] - 1.625*U[22]*U[29] + 2.275*U[22]*U[31] - 2.275*U[23]*U[30] - 0.00122336344718992*U[32]\n", + "\tU_out[33] = 0.00226673637054264*U[15] + 1.3*U[20]*U[30] + 1.95*U[21]*U[31] - 1.3*U[22]*U[28] - 1.95*U[23]*U[29] - 0.00122336344718992*U[33]\n", + "\tU_out[34] = -0.325*U[20]*U[29] + 1.625*U[20]*U[31] + 0.325*U[21]*U[28] - 1.625*U[23]*U[28] - 0.00122336344718992*U[34]\n", + "\tU_out[35] = -0.65*U[20]*U[30] + 0.65*U[22]*U[28] - 0.00122336344718992*U[35]\n", + "\treturn U_out" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77155759", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f38b345f", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "ten.test_tensor_numerically(tol=1e-12)" + "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0f5941", + "metadata": {}, + "outputs": [], + "source": [ + "integrator = RungeKuttaIntegrator()\n", + "integrator.set_func(f)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "415ab585", + "metadata": {}, + "outputs": [], + "source": [ + "integrator_num = RungeKuttaIntegrator()\n", + "integrator_num.set_func(f_num)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2b5f14a", + "metadata": {}, + "outputs": [], + "source": [ + "ic = np.array([ 0.03099396, -0.00496147, 0.00173704, -0.00135697, -0.00772211,\n", + " -0.01966693, 0.0133646 , 0.001414 , 0.01117828, -0.00041981,\n", + " 0.0296539 , -0.00059846, -0.0054299 , 0.00265086, -0.00813694,\n", + " -0.01703874, 0.00775618, -0.00437281, 0.00628166, -0.00235651,\n", + " -0.00471975, 0.00143941, -0.00162457, -0.00087876, 0.00399296,\n", + " -0.00265117, -0.00265747, 0.00141623, -0.0210313 , 0.05039241,\n", + " 0.01445278, 0.00645707, -0.04806996, -0.06529819, -0.00455508,\n", + " 0.04942686])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c6d5773", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", + "reference_time, reference_traj = integrator.get_trajectories()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b38989ba", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator_num.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", + "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" ] }, { "cell_type": "code", "execution_count": null, - "id": "6aabc02f", + "id": "2f892676", "metadata": {}, "outputs": [], "source": [ - "ten._set_symbolic_tensor()" + "varx = 21\n", + "vary = 29\n", + "plt.figure(figsize=(12, 10))\n", + "\n", + "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", + "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", + "\n", + "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" ] }, { "cell_type": "code", "execution_count": null, - "id": "a60e7937", + "id": "6697d107", "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(ten.tensor)" + "plt.figure(figsize=(12, 8))\n", + "\n", + "plt.plot(reference_traj_num[varx, :] - reference_traj[varx, :])\n", + "plt.plot(reference_traj_num[vary, :] - reference_traj[vary, :])\n", + "plt.show()" ] }, { "cell_type": "code", "execution_count": null, - "id": "9c8c57bd", + "id": "978ac07b", "metadata": {}, "outputs": [], "source": [] diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index 431ef55..cd55b86 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -35,6 +35,19 @@ "from qgs.functions.tendencies import create_tendencies" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "8500ea89", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", + "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", + "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", + "from qgs.tensors.symbolic_qgtensor import _shift_dict_keys, _add_to_dict" + ] + }, { "cell_type": "code", "execution_count": null, @@ -42,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "model_parameters = QgParams(dynamic_T=True)" + "model_parameters = QgParams({'n': 1.5}, dynamic_T=True)" ] }, { @@ -71,14 +84,38 @@ { "cell_type": "code", "execution_count": null, - "id": "8500ea89", + "id": "d2ef57b2", "metadata": {}, "outputs": [], "source": [ - "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", - "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", - "from qgs.tensors.symbolic_qgtensor import _shift_dict_keys, _add_to_dict" + "# Setting MAOOAM parameters according to the publication linked above\n", + "model_parameters.set_params({'kd': 0.0290, 'kdp': 0.0290, 'r': 1.e-7,\n", + " 'h': 136.5, 'd': 1.1e-7})\n", + "model_parameters.atemperature_params.set_params({'eps': 0.7, 'hlambda': 15.06})\n", + "model_parameters.gotemperature_params.set_params({'gamma': 5.6e8})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d9733db", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.atemperature_params.set_insolation(103., 0)\n", + "model_parameters.atemperature_params.set_insolation(103., 1)\n", + "model_parameters.gotemperature_params.set_insolation(310., 0)\n", + "model_parameters.gotemperature_params.set_insolation(310., 1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c0fbea9", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.print_params()" ] }, { @@ -147,6 +184,16 @@ "ocn_loaded.load_from_file('temp_ocn_sym_de.ip')" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f1ffbb0", + "metadata": {}, + "outputs": [], + "source": [ + "ocn_num = OceanicSymbolicInnerProducts(model_parameters, stored=True)" + ] + }, { "cell_type": "markdown", "id": "d43d341b", @@ -158,11 +205,11 @@ { "cell_type": "code", "execution_count": null, - "id": "2e17b891", + "id": "e38656d8", "metadata": {}, "outputs": [], "source": [ - "ten = SymbolicTensorDynamicT(params=model_parameters, atmospheric_inner_products=atm_loaded, oceanic_inner_products=ocn_loaded)" + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, print_equations, equation_as_function" ] }, { @@ -174,79 +221,107 @@ }, "outputs": [], "source": [ - "_, _, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" + "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "8ed132e4", + "id": "c73f1cd9", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "# ten.test_tensor_numerically(tol=1e-9)" + "%%time\n", + "funcs = create_symbolic_equations(model_parameters, atm_loaded, ocn_loaded)" ] }, { "cell_type": "code", "execution_count": null, - "id": "c6dd6324", + "id": "c3aaf1ae", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "# atm_num = AtmosphericSymbolicInnerProducts(model_parameters, stored=True)\n", - "# ocn_num = OceanicSymbolicInnerProducts(model_parameters, stored=True)\n", - "\n", - "# ocn_num_ns = OceanicSymbolicInnerProducts(model_parameters, stored=False)" + "ten = SymbolicTensorDynamicT(model_parameters, atm_loaded, ocn_loaded, numerically_test_tensor=False)" ] }, { "cell_type": "code", "execution_count": null, - "id": "e38656d8", + "id": "6ac0c25c", "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, print_equations, equation_as_function" + "ten.print_tensor(ten.tensor_dic)" ] }, { "cell_type": "code", "execution_count": null, - "id": "c73f1cd9", + "id": "79c77094", + "metadata": {}, + "outputs": [], + "source": [ + "ten.print_tensor(ten_num.tensor.todense())" + ] + }, + { + "cell_type": "markdown", + "id": "3584b521", + "metadata": {}, + "source": [ + "I the error is comming from the _O and _C terms having some non zeros that should be zero" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7d8fe02f", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "%%time\n", - "funcs = create_symbolic_equations(model_parameters, atm_loaded, ocn_loaded)" + "ten.print_tensor(ocn_loaded._C)" ] }, { "cell_type": "code", "execution_count": null, - "id": "27797dd9", + "id": "aa2d7a41", "metadata": {}, "outputs": [], "source": [ - "model_parameters._set_symbolic_parameters()" + "ten.print_tensor(ocn_num._O.todense())" ] }, { "cell_type": "code", "execution_count": null, - "id": "e880cb26", + "id": "3ce94a64", "metadata": {}, "outputs": [], - "source": [ - "%%time\n", - "eqs = print_equations(funcs[0], model_parameters, subs=True, return_equations=True)" - ] + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7eb27d00", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e63015c3", + "metadata": {}, + "outputs": [], + "source": [] }, { "cell_type": "code", @@ -256,7 +331,7 @@ "outputs": [], "source": [ "%%time\n", - "fx = equation_as_function(funcs[0], model_parameters, subs=True, language='python', string_output=True)" + "fx = equation_as_function(funcs, model_parameters, subs=True, language='julia', string_output=True)" ] }, { @@ -291,44 +366,44 @@ "def f(t, U):\n", " #Tendency function of the qgs model\n", " U_out = np.empty_like(U)\n", - " U_out[0] = -0.05*U[0] + 0.05*U[11] + 0.168986428105931*U[22] + 0.251005505082614*U[24]\n", - " U_out[1] = -0.980418808722262*U[0]*U[2] - 0.980418808722262*U[11]*U[13] + 0.05*U[12] - 1.56867009395562*U[14]*U[16] - 1.50055762081784*U[15]*U[18] + 1.50055762081784*U[16]*U[17] - 0.05*U[1] - 0.0112217054051163*U[21] + 0.120745727252785*U[2] - 1.56867009395562*U[3]*U[5] - 1.50055762081784*U[4]*U[7] + 1.50055762081784*U[5]*U[6]\n", - " U_out[2] = 0.980418808722262*U[0]*U[1] + 0.980418808722262*U[11]*U[12] + 0.05*U[13] + 1.56867009395562*U[14]*U[15] + 1.50055762081784*U[15]*U[17] + 1.50055762081784*U[16]*U[18] - 0.120745727252785*U[1] + 0.05*U[25] - 0.05*U[2] + 1.56867009395562*U[3]*U[4] + 1.50055762081784*U[4]*U[6] + 1.50055762081784*U[5]*U[7]\n", - " U_out[3] = 1.87265793760678*U[12]*U[16] - 1.87265793760678*U[13]*U[15] + 0.05*U[14] + 3.74531587521356*U[17]*U[20] - 3.74531587521356*U[18]*U[19] + 1.87265793760678*U[1]*U[5] - 0.00679432430697251*U[21] + 0.0810088839426413*U[23] - 1.87265793760678*U[2]*U[4] - 0.05*U[3] + 3.74531587521356*U[6]*U[9] - 3.74531587521356*U[7]*U[8]\n", - " U_out[4] = -1.02902937637678*U[0]*U[5] - 1.02902937637678*U[11]*U[16] + 1.73752196836555*U[12]*U[18] + 0.574852231579352*U[13]*U[14] - 1.73752196836555*U[13]*U[17] + 0.05*U[15] + 1.73752196836555*U[1]*U[7] - 0.0164935614721478*U[22] + 0.574852231579352*U[2]*U[3] - 1.73752196836555*U[2]*U[6] - 0.05*U[4] + 0.0570836566449895*U[5]\n", - " U_out[5] = 1.02902937637678*U[0]*U[4] + 1.02902937637678*U[11]*U[15] - 0.574852231579352*U[12]*U[14] - 1.73752196836555*U[12]*U[17] - 1.73752196836555*U[13]*U[18] + 0.05*U[16] - 0.574852231579352*U[1]*U[3] - 1.73752196836555*U[1]*U[6] + 0.05*U[26] - 1.73752196836555*U[2]*U[7] - 0.0570836566449895*U[4] - 0.05*U[5]\n", - " U_out[6] = -2.71889339738442*U[0]*U[7] - 2.71889339738442*U[11]*U[18] + 0.753865979381443*U[12]*U[16] + 0.753865979381443*U[13]*U[15] - 4.35022943581506*U[14]*U[20] + 0.05*U[17] + 0.753865979381443*U[1]*U[5] - 0.000777999678859866*U[21] + 0.753865979381443*U[2]*U[4] - 4.35022943581506*U[3]*U[9] - 0.05*U[6] + 0.0837128882242243*U[7]\n", - " U_out[7] = 2.71889339738442*U[0]*U[6] + 2.71889339738442*U[11]*U[17] - 0.753865979381443*U[12]*U[15] + 0.753865979381443*U[13]*U[16] + 4.35022943581506*U[14]*U[19] + 0.05*U[18] - 0.753865979381443*U[1]*U[4] + 0.753865979381443*U[2]*U[5] + 4.35022943581506*U[3]*U[8] - 0.0837128882242243*U[6] - 0.05*U[7]\n", - " U_out[8] = -2.26482546109568*U[0]*U[9] - 2.26482546109568*U[11]*U[20] - 1.7450294536311*U[14]*U[18] + 0.05*U[19] - 0.0017443933973331*U[22] - 1.7450294536311*U[3]*U[7] - 0.05*U[8] + 0.0603728636263923*U[9]\n", - " U_out[9] = 2.26482546109568*U[0]*U[8] + 2.26482546109568*U[11]*U[19] + 1.7450294536311*U[14]*U[17] + 0.05*U[20] + 1.7450294536311*U[3]*U[6] - 0.0603728636263923*U[8] - 0.05*U[9]\n", - " U_out[10] = -0.00055453242558364*U[10]**4 - 0.0193798449612403*U[10] - 0.00576517534924902*U[21] - 0.0127293180382657*U[23] + 1.73291382994887e-5*U[29]**4 + 5.6185881728191e-5*U[29]**3*U[30] + 1.87286272427303e-5*U[29]**3*U[32] + 0.00968992248062015*U[29] + 0.00785435532111145*U[30] + 0.00261811844037048*U[32] + 0.000515537550678319\n", - " U_out[11] = 0.00454545454545455*U[0] - 0.00201648154757687*U[10]**3*U[11] - 0.0239816772374912*U[11] - 1.41868025576271*U[12]*U[2] + 1.41868025576271*U[13]*U[1] - 1.13494420461017*U[15]*U[5] + 1.13494420461017*U[16]*U[4] - 2.83736051152543*U[17]*U[7] + 2.83736051152543*U[18]*U[6] - 2.26988840922034*U[19]*U[9] + 2.26988840922034*U[20]*U[8] - 0.0153624025550847*U[22] - 0.0228186822802377*U[24] + 4.81568702750899e-5*U[29]**3*U[31] + 1.9262748110036e-5*U[29]**3*U[33] + 0.00673196110231097*U[31] + 0.00269278444092439*U[33] + 0.000468670500616653\n", - " U_out[12] = -1.43757363347933*U[0]*U[13] - 0.00174793514762377*U[10]**3*U[12] + 1.02191932371371*U[11]*U[2] - 0.030110200915083*U[12] + 0.0255954299692664*U[13] + 1.63507091794193*U[14]*U[5] + 1.21855791962175*U[15]*U[7] - 2.30011781356693*U[16]*U[3] - 1.21855791962175*U[16]*U[6] + 1.85472813238771*U[17]*U[5] - 1.85472813238771*U[18]*U[4] + 0.0105988967691095*U[1] + 0.00237875394324372*U[21] - 2.31827099123654e-5*U[29]**3*U[30] - 0.00324076503486835*U[30]\n", - " U_out[13] = 1.43757363347933*U[0]*U[12] - 0.00174793514762377*U[10]**3*U[13] - 1.02191932371371*U[11]*U[1] - 0.0255954299692664*U[12] - 0.030110200915083*U[13] - 1.63507091794193*U[14]*U[4] + 2.30011781356693*U[15]*U[3] - 1.21855791962175*U[15]*U[6] - 1.21855791962175*U[16]*U[7] + 1.85472813238771*U[17]*U[4] + 1.85472813238771*U[18]*U[5] - 0.0105988967691095*U[25] + 5.46229733632427e-5*U[29]**3*U[34] + 0.0105988967691095*U[2] + 0.00763587271916482*U[34]\n", - " U_out[14] = -0.0015843783588104*U[10]**3*U[14] - 1.24843862507119*U[12]*U[5] + 1.24843862507119*U[13]*U[4] - 0.0338427464008859*U[14] - 2.3185288751322*U[15]*U[2] + 2.3185288751322*U[16]*U[1] - 2.49687725014238*U[17]*U[9] + 2.49687725014238*U[18]*U[8] - 4.63705775026441*U[19]*U[7] + 4.63705775026441*U[20]*U[6] + 0.00194123551627786*U[21] - 0.0231453954121832*U[23] - 1.89187704652139e-5*U[29]**3*U[30] + 3.4053786837385e-5*U[29]**3*U[32] - 0.00264469900447931*U[30] + 0.00476045820806276*U[32] + 0.0142857142857143*U[3]\n", - " U_out[15] = -1.16886956037576*U[0]*U[16] - 0.00141372192628079*U[10]**3*U[15] + 0.422511733532696*U[11]*U[5] - 0.612715105162524*U[12]*U[7] - 1.38291034440645*U[13]*U[3] + 0.612715105162524*U[13]*U[6] + 1.79985224341047*U[14]*U[2] - 0.0377373135508224*U[15] + 0.0207014663040147*U[16] - 1.87294455066922*U[17]*U[2] + 1.87294455066922*U[18]*U[1] + 0.00598141266899433*U[22] - 1.87500693937487e-5*U[29]**3*U[31] - 0.00262111588862201*U[31] + 0.0181325685149777*U[4]\n", - " U_out[16] = 1.16886956037576*U[0]*U[15] - 0.00141372192628079*U[10]**3*U[16] - 0.422511733532696*U[11]*U[4] + 1.38291034440645*U[12]*U[3] + 0.612715105162524*U[12]*U[6] + 0.612715105162524*U[13]*U[7] - 1.79985224341047*U[14]*U[1] - 0.0207014663040147*U[15] - 0.0377373135508224*U[16] - 1.87294455066922*U[17]*U[1] - 1.87294455066922*U[18]*U[2] - 0.0181325685149777*U[26] + 4.41788101962747e-5*U[29]**3*U[35] + 0.0061758588149268*U[35] + 0.0181325685149777*U[5]\n", - " U_out[17] = -2.94535914360826*U[0]*U[18] - 0.00124894690446766*U[10]**3*U[17] + 0.569389237785845*U[11]*U[7] - 0.768581081081081*U[12]*U[5] - 0.768581081081081*U[13]*U[4] + 0.911022780457353*U[14]*U[9] + 1.42736486486486*U[15]*U[2] + 1.42736486486486*U[16]*U[1] - 0.0414976604511488*U[17] + 0.0365772529628368*U[18] - 4.71257462977322*U[20]*U[3] + 0.000339936796618951*U[21] - 3.31293455842248e-6*U[29]**3*U[30] - 0.000463122841131525*U[30] + 0.0218468468468468*U[6]\n", - " U_out[18] = 2.94535914360826*U[0]*U[17] - 0.00124894690446766*U[10]**3*U[18] - 0.569389237785845*U[11]*U[6] + 0.768581081081081*U[12]*U[4] - 0.768581081081081*U[13]*U[5] - 0.911022780457353*U[14]*U[8] - 1.42736486486486*U[15]*U[1] + 1.42736486486486*U[16]*U[2] - 0.0365772529628368*U[17] - 0.0414976604511488*U[18] + 4.71257462977322*U[19]*U[3] + 0.0218468468468468*U[7]\n", - " U_out[19] = -2.37660377951895*U[0]*U[20] - 0.00106846324775268*U[10]**3*U[19] + 0.0288656329496229*U[11]*U[9] + 1.50101291338039*U[14]*U[7] - 3.30992591155675*U[18]*U[3] - 0.0456164956460695*U[19] + 0.031291522765895*U[20] + 0.000904126828290182*U[22] - 2.83418678986432e-6*U[29]**3*U[31] - 0.000396197575072056*U[31] + 0.0259152215799615*U[8]\n", - " U_out[20] = 2.37660377951895*U[0]*U[19] - 0.00106846324775268*U[10]**3*U[20] - 0.0288656329496229*U[11]*U[8] - 1.50101291338039*U[14]*U[6] + 3.30992591155675*U[17]*U[3] - 0.031291522765895*U[19] - 0.0456164956460695*U[20] + 0.0259152215799615*U[9]\n", - " U_out[21] = 6.35094981132412e-8*U[12] + 8.50239961075093e-8*U[14] + 3.66419111790894e-8*U[17] - 6.35094981132412e-8*U[1] - 1.58263150747531e-7*U[21] - 0.000969739516006511*U[22]*U[25] - 0.00116937805032627*U[22]*U[27] - 0.00278561057700379*U[23]*U[26] - 0.00308506837848344*U[23]*U[28] - 0.00534779391583196*U[24]*U[27] - 7.91389255433123e-5*U[25] - 8.50239961075093e-8*U[3] - 3.66419111790894e-8*U[6]\n", - " U_out[22] = 4.2438907259419e-8*U[0] - 4.2438907259419e-8*U[11] + 1.34106967724862e-7*U[15] + 5.07202450868017e-8*U[19] - 0.000708243330589963*U[21]*U[25] + 0.00172613939940631*U[21]*U[27] - 4.91188341087484e-7*U[22] - 0.00626988589506499*U[23]*U[25] - 0.0119940379417069*U[24]*U[26] - 7.9002862144845e-5*U[26] - 1.34106967724862e-7*U[4] - 5.07202450868017e-8*U[8]\n", - " U_out[23] = -1.52343526260616e-7*U[14] - 0.00317032731211854*U[21]*U[26] + 0.0060425658523595*U[21]*U[28] + 0.00160884360522711*U[22]*U[25] - 1.04352693785129e-6*U[23] - 0.0178532887343688*U[24]*U[25] - 7.87771265478973e-5*U[27] + 1.52343526260616e-7*U[3]\n", - " U_out[24] = 1.68596160851496e-8*U[0] - 1.68596160851496e-8*U[11] - 0.00857174744319731*U[21]*U[27] - 0.00140681174654471*U[22]*U[26] + 0.00871788567853858*U[23]*U[25] - 1.8115193420227e-6*U[24] - 7.84632552652771e-5*U[28]\n", - " U_out[25] = -1.49531921957748e-7*U[13] + 0.00167798163428125*U[21]*U[22] + 7.9081381622411e-5*U[21] + 0.0046610600952257*U[22]*U[23] + 0.00913567778664237*U[23]*U[24] - 2.99063843915495e-7*U[25] + 1.49531921957748e-7*U[2]\n", - " U_out[26] = -3.15752734584711e-7*U[16] + 0.0059559067658186*U[21]*U[23] + 0.0134007902230919*U[22]*U[24] + 7.89455158524528e-5*U[22] - 6.31505469169422e-7*U[26] + 3.15752734584711e-7*U[5]\n", - " U_out[27] = -0.0005567719890759*U[21]*U[22] + 0.0139192997268975*U[21]*U[24] + 7.8720107381223e-5*U[23] - 1.18304364206348e-6*U[27]\n", - " U_out[28] = -0.00295762797162433*U[21]*U[23] + 7.84066893926385e-5*U[24] - 1.94992690884148e-6*U[28]\n", - " U_out[29] = 2.7726621279182e-5*U[10]**4 + 0.000141323097779036*U[10]**3*U[12] + 5.0894196312133e-5*U[10]**3*U[14] + 2.82646195558073e-5*U[10]**3*U[17] + 0.00193798449615121*U[10] + 0.00246948564053933*U[12] + 0.000889327285869774*U[14] + 0.000493897128107866*U[17] + 1.30103637962932*U[21]*U[35] - 0.650518189814661*U[21]*U[37] - 1.30103637961282*U[22]*U[34] + 0.975777284716489*U[22]*U[36] - 3.90310913886596*U[23]*U[35] + 1.95155456943298*U[23]*U[37] - 2.27681366435131*U[24]*U[34] - 4.87888642358245*U[24]*U[36] + 1.30103637961282*U[25]*U[31] + 2.27681366435131*U[25]*U[33] - 1.30103637962932*U[26]*U[30] + 3.90310913886596*U[26]*U[32] - 0.975777284716489*U[27]*U[31] + 4.87888642358245*U[27]*U[33] + 0.650518189814661*U[28]*U[30] - 1.95155456943298*U[28]*U[32] - 2.28014977624852e-6*U[29]**4 + 8.470329472543e-22*U[29]**3*U[30] + 2.5410988417629e-21*U[29]**3*U[32] - 0.000968992248062015*U[29] + 0.000180438142739942\n", - " U_out[30] = -3.3881317890172e-21*U[10]**4 - 0.000161622362549857*U[10]**3*U[12] - 8.36313275290456e-5*U[10]**3*U[14] - 3.23244725099714e-5*U[10]**3*U[17] - 3.96655364803822e-16*U[10] - 0.00282419583055875*U[12] - 0.00146137726726263*U[14] - 0.000564839166111749*U[17] - 1.05458036756628*U[21]*U[35] + 0.52729018378314*U[21]*U[37] + 2.02958036756658*U[22]*U[34] - 1.11593527567481*U[22]*U[36] + 4.46374110269924*U[23]*U[35] - 2.23187055134962*U[23]*U[37] + 1.84551564324099*U[24]*U[34] + 5.57967637837405*U[24]*U[36] - 2.02958036756658*U[25]*U[31] - 1.84551564324099*U[25]*U[33] + 1.05458036756628*U[26]*U[30] - 4.46374110269924*U[26]*U[32] + 1.11593527567481*U[27]*U[31] - 5.57967637837405*U[27]*U[33] - 0.52729018378314*U[28]*U[30] + 2.23187055134962*U[28]*U[32] - 8.470329472543e-22*U[29]**4 - 9.12059910499407e-6*U[29]**3*U[30] - 2.5410988417629e-21*U[29]**3*U[32] - 0.000968992248062015*U[30] - 3.68628738645072e-17\n", - " U_out[31] = 8.47560916841583e-5*U[10]**3*U[11] - 4.70701742060666e-5*U[10]**3*U[15] - 9.41403484121332e-6*U[10]**3*U[19] + 0.00148103144250841*U[11] - 0.000822506165849588*U[15] - 0.000164501233169918*U[19] - 0.975*U[21]*U[34] + 0.325*U[21]*U[36] + 1.625*U[23]*U[34] + 1.95*U[24]*U[35] + 0.975*U[25]*U[30] - 1.625*U[25]*U[32] - 1.95*U[26]*U[33] - 0.325*U[27]*U[30] - 9.12059910499408e-6*U[29]**3*U[31] - 0.000968992248062015*U[31] + 0.000137893034416115\n", - " U_out[32] = -3.3881317890172e-21*U[10]**4 - 3.81840627812635e-5*U[10]**3*U[12] + 6.25293886200869e-5*U[10]**3*U[14] - 7.63681255625269e-6*U[10]**3*U[17] - 2.16840434497101e-19*U[10] - 0.0006672298882363*U[12] + 0.00109264111625484*U[14] - 0.00013344597764726*U[17] - 1.65152678918876*U[21]*U[35] + 0.82576339459438*U[21]*U[37] - 1.27347321081124*U[22]*U[34] - 0.26364509189157*U[22]*U[36] + 1.05458036756628*U[23]*U[35] - 0.527290183783139*U[23]*U[37] + 2.89017188108033*U[24]*U[34] + 1.31822545945785*U[24]*U[36] + 1.27347321081124*U[25]*U[31] - 2.89017188108033*U[25]*U[33] + 1.65152678918876*U[26]*U[30] - 1.05458036756628*U[26]*U[32] + 0.26364509189157*U[27]*U[31] - 1.31822545945785*U[27]*U[33] - 0.82576339459438*U[28]*U[30] + 0.527290183783139*U[28]*U[32] + 2.11758236813575e-22*U[29]**4 - 9.12059910499408e-6*U[29]**3*U[32] - 0.000968992248062015*U[32] + 2.71050543121376e-20\n", - " U_out[33] = 3.39024366736633e-5*U[10]**3*U[11] + 0.000592412577003366*U[11] - 1.625*U[21]*U[36] - 1.95*U[22]*U[35] - 2.275*U[23]*U[34] + 2.275*U[25]*U[32] + 1.95*U[26]*U[31] + 1.625*U[27]*U[30] - 9.12059910499408e-6*U[29]**3*U[33] - 0.000968992248062015*U[33] + 5.51572137664459e-5\n", - " U_out[34] = 0.000110906485116728*U[10]**3*U[13] + 0.00193798449612403*U[13] + 0.975*U[21]*U[31] - 0.975*U[22]*U[30] + 1.625*U[22]*U[32] - 1.625*U[23]*U[31] + 2.275*U[23]*U[33] - 2.275*U[24]*U[32] - 9.12059910499408e-6*U[29]**3*U[34] - 0.000968992248062015*U[34]\n", - " U_out[35] = 0.000110906485116728*U[10]**3*U[16] + 0.00193798449612403*U[16] + 1.3*U[21]*U[32] + 1.95*U[22]*U[33] - 1.3*U[23]*U[30] - 1.95*U[24]*U[31] - 9.12059910499408e-6*U[29]**3*U[35] - 0.000968992248062015*U[35]\n", - " U_out[36] = -0.325*U[21]*U[31] + 1.625*U[21]*U[33] + 0.325*U[22]*U[30] - 1.625*U[24]*U[30] - 9.12059910499408e-6*U[29]**3*U[36] - 0.000968992248062015*U[36]\n", - " U_out[37] = -0.65*U[21]*U[32] + 0.65*U[23]*U[30] - 9.12059910499408e-6*U[29]**3*U[37] - 0.000968992248062015*U[37]\n", + " U_out[0] = -0.0145*U[0] + 0.0145*U[11] + 0.0505574149661188*U[22] + 0.0734121368001177*U[24]\n", + " U_out[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[11]*U[13] + 0.0145*U[12] - 1.9945469157942*U[14]*U[16] - 2.59615384615385*U[15]*U[18] + 2.59615384615385*U[16]*U[17] - 0.0145*U[1] - 0.00295864958311857*U[21] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", + " U_out[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[11]*U[12] + 0.0145*U[13] + 1.9945469157942*U[14]*U[15] + 2.59615384615385*U[15]*U[17] + 2.59615384615385*U[16]*U[18] - 0.115315741885204*U[1] + 0.0145*U[25] - 0.0145*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", + " U_out[3] = 2.16075915877706*U[12]*U[16] - 2.16075915877706*U[13]*U[15] + 0.0145*U[14] + 4.32151831755411*U[17]*U[20] - 4.32151831755411*U[18]*U[19] + 2.16075915877706*U[1]*U[5] - 0.00216427290094687*U[21] + 0.0238416302768307*U[23] - 2.16075915877706*U[2]*U[4] - 0.0145*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", + " U_out[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[11]*U[16] + 2.43*U[12]*U[18] + 0.345721465404329*U[13]*U[14] - 2.43*U[13]*U[17] + 0.0145*U[15] + 2.43*U[1]*U[7] - 0.00449241352700723*U[22] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.0145*U[4] + 0.0599641857803059*U[5]\n", + " U_out[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[11]*U[15] - 0.345721465404329*U[12]*U[14] - 2.43*U[12]*U[17] - 2.43*U[13]*U[18] + 0.0145*U[16] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.0145*U[26] - 2.43*U[2]*U[7] - 0.0599641857803059*U[4] - 0.0145*U[5]\n", + " U_out[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[11]*U[18] + 0.675*U[12]*U[16] + 0.675*U[13]*U[15] - 5.18582198106493*U[14]*U[20] + 0.0145*U[17] + 0.675*U[1]*U[5] - 0.000192312222902707*U[21] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.0145*U[6] + 0.0749552322253824*U[7]\n", + " U_out[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[11]*U[17] - 0.675*U[12]*U[15] + 0.675*U[13]*U[16] + 5.18582198106493*U[14]*U[19] + 0.0145*U[18] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.0145*U[7]\n", + " U_out[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[11]*U[20] - 2.65939588772561*U[14]*U[18] + 0.0145*U[19] - 0.000431962839135311*U[22] - 2.65939588772561*U[3]*U[7] - 0.0145*U[8] + 0.0576578709426019*U[9]\n", + " U_out[9] = 2.65939588772561*U[0]*U[8] + 2.65939588772561*U[11]*U[19] + 2.65939588772561*U[14]*U[17] + 0.0145*U[20] + 2.65939588772561*U[3]*U[6] - 0.0576578709426019*U[8] - 0.0145*U[9]\n", + " U_out[10] = -0.000510753549879668*U[10]**4 - 0.014593023255814*U[10] - 0.00183644645351737*U[21] - 0.00374635076517544*U[23] + 1.59610484337396e-5*U[29]**4 + 5.17501542233338e-5*U[29]**3*U[30] + 1.72500514077779e-5*U[29]**3*U[32] + 0.00729651162790698*U[29] + 0.00591432955679692*U[30] + 0.00197144318559898*U[32] + 0.000531003677198668\n", + " U_out[11] = 0.00131818181818182*U[0] - 0.00185728563592607*U[10]**3*U[11] - 0.0198572938689218*U[11] - 1.63693875664928*U[12]*U[2] + 1.63693875664928*U[13]*U[1] - 1.30955100531943*U[15]*U[5] + 1.30955100531943*U[16]*U[4] - 3.27387751329857*U[17]*U[7] + 3.27387751329857*U[18]*U[6] - 2.61910201063885*U[19]*U[9] + 2.61910201063885*U[20]*U[8] - 0.00459612863328353*U[22] - 0.00667383061819252*U[24] + 4.43550120954776e-5*U[29]**3*U[31] + 1.7742004838191e-5*U[29]**3*U[33] + 0.00506916671004016*U[31] + 0.00202766668401607*U[33] + 0.000482730615635153\n", + " U_out[12] = -1.6647358298754*U[0]*U[13] - 0.0015418975090707*U[10]**3*U[12] + 1.05320021890077*U[11]*U[2] - 0.0287966213251426*U[12] + 0.0282849932925971*U[13] + 1.68512035024123*U[14]*U[5] + 1.06132075471698*U[15]*U[7] - 2.66357732780065*U[16]*U[3] - 1.06132075471698*U[16]*U[6] + 2.33490566037736*U[17]*U[5] - 2.33490566037736*U[18]*U[4] + 0.00355660377358491*U[1] + 0.000725706501519649*U[21] - 2.04500508591402e-5*U[29]**3*U[30] - 0.00233715902975374*U[30]\n", + " U_out[13] = 1.6647358298754*U[0]*U[12] - 0.0015418975090707*U[10]**3*U[13] - 1.05320021890077*U[11]*U[1] - 0.0282849932925971*U[12] - 0.0287966213251426*U[13] - 1.68512035024123*U[14]*U[4] + 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] - 1.06132075471698*U[16]*U[7] + 2.33490566037736*U[17]*U[4] + 2.33490566037736*U[18]*U[5] - 0.00355660377358491*U[25] + 4.81842971584593e-5*U[29]**3*U[34] + 0.00355660377358491*U[2] + 0.00550680122860904*U[34]\n", + " U_out[14] = -0.00145929585679905*U[10]**3*U[14] - 1.44050610585137*U[12]*U[5] + 1.44050610585137*U[13]*U[4] - 0.0311378737541528*U[14] - 2.67522562515254*U[15]*U[2] + 2.67522562515254*U[16]*U[1] - 2.88101221170274*U[17]*U[9] + 2.88101221170274*U[18]*U[8] - 5.35045125030509*U[19]*U[7] + 5.35045125030509*U[20]*U[6] + 0.000618363685984819*U[21] - 0.00681189436480877*U[23] - 1.74251833232233e-5*U[29]**3*U[30] + 3.1365329981802e-5*U[29]**3*U[32] - 0.00199145835037292*U[30] + 0.00358462503067126*U[32] + 0.00414285714285714*U[3]\n", + " U_out[15] = -1.35185957626052*U[0]*U[16] - 0.00125723950739611*U[10]**3*U[15] + 0.421071015556554*U[11]*U[5] - 0.45*U[12]*U[7] - 1.63996079743079*U[13]*U[3] + 0.45*U[13]*U[6] + 1.90590038620335*U[14]*U[2] - 0.0368649373881932*U[15] + 0.0230631483770407*U[16] - 2.31923076923077*U[17]*U[2] + 2.31923076923077*U[18]*U[1] + 0.00172785135654124*U[22] - 1.66746568543758e-5*U[29]**3*U[31] - 0.00190568351656843*U[31] + 0.00557692307692308*U[4]\n", + " U_out[16] = 1.35185957626052*U[0]*U[15] - 0.00125723950739611*U[10]**3*U[16] - 0.421071015556554*U[11]*U[4] + 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 0.45*U[13]*U[7] - 1.90590038620335*U[14]*U[1] - 0.0230631483770407*U[15] - 0.0368649373881932*U[16] - 2.31923076923077*U[17]*U[1] - 2.31923076923077*U[18]*U[2] - 0.00557692307692308*U[26] + 3.92887346061283e-5*U[29]**3*U[35] + 0.00449016100178891*U[35] + 0.00557692307692308*U[5]\n", + " U_out[17] = -3.421202001397*U[0]*U[18] - 0.00102150709975934*U[10]**3*U[17] + 0.180063263231421*U[11]*U[7] - 0.7875*U[12]*U[5] - 0.7875*U[13]*U[4] + 0.288101221170275*U[14]*U[9] + 1.4625*U[15]*U[2] + 1.4625*U[16]*U[1] - 0.043546511627907*U[17] + 0.0374776161126912*U[18] - 5.47392320223521*U[20]*U[3] + 9.61561114513534e-5*U[21] - 2.70963173883607e-6*U[29]**3*U[30] - 0.00030967357144237*U[30] + 0.00725*U[6]\n", + " U_out[18] = 3.421202001397*U[0]*U[17] - 0.00102150709975934*U[10]**3*U[18] - 0.180063263231421*U[11]*U[6] + 0.7875*U[12]*U[4] - 0.7875*U[13]*U[5] - 0.288101221170275*U[14]*U[8] - 1.4625*U[15]*U[1] + 1.4625*U[16]*U[2] - 0.0374776161126912*U[17] - 0.043546511627907*U[18] + 5.47392320223521*U[19]*U[3] + 0.00725*U[7]\n", + " U_out[19] = -2.75575081119393*U[0]*U[20] - 0.000888267043268988*U[10]**3*U[19] - 0.250522801017629*U[11]*U[9] + 1.00209120407052*U[14]*U[7] - 4.00836481628207*U[18]*U[3] - 0.0473230535894843*U[19] + 0.0325892314023402*U[20] + 0.00024415290907648*U[22] - 2.35620151203137e-6*U[29]**3*U[31] - 0.000269281366471626*U[31] + 0.00819565217391304*U[8]\n", + " U_out[20] = 2.75575081119393*U[0]*U[19] - 0.000888267043268988*U[10]**3*U[20] + 0.250522801017629*U[11]*U[8] - 1.00209120407052*U[14]*U[6] + 4.00836481628207*U[17]*U[3] - 0.0325892314023402*U[19] - 0.0473230535894843*U[20] + 0.00819565217391304*U[9]\n", + " U_out[21] = 2.30554431681946e-7*U[12] + 2.5547312812995e-7*U[14] + 1.41879650265813e-7*U[17] - 2.30554431681946e-7*U[1] - 4.98594961275655e-7*U[21] - 0.000231547500016414*U[22]*U[25] - 0.000393263214313592*U[22]*U[27] - 0.00077917571434095*U[23]*U[26] - 0.00102174928578672*U[23]*U[28] - 0.00156202678582502*U[24]*U[27] - 2.49430228227486e-5*U[25] - 2.5547312812995e-7*U[3] - 1.41879650265813e-7*U[6]\n", + " U_out[22] = 1.27676499203106e-7*U[0] - 1.27676499203106e-7*U[11] + 4.43165421807241e-7*U[15] + 1.84356815471812e-7*U[19] - 0.000297563940911795*U[21]*U[25] + 0.00056941247952257*U[21]*U[27] - 1.45521268835226e-6*U[22] - 0.00185518259457354*U[23]*U[25] - 0.00363689261114416*U[24]*U[26] - 2.49312940023328e-5*U[26] - 4.43165421807241e-7*U[4] - 1.84356815471812e-7*U[8]\n", + " U_out[23] = -4.59275459509595e-7*U[14] - 0.00110122563125871*U[21]*U[26] + 0.0019601816236405*U[21]*U[28] + 0.000385428970940548*U[22]*U[25] - 3.04757790976247e-6*U[23] - 0.00547309138735578*U[24]*U[25] - 2.49117704610167e-5*U[27] + 4.59275459509595e-7*U[3]\n", + " U_out[24] = 5.09747214298137e-8*U[0] - 5.09747214298137e-8*U[11] - 0.0028417174170155*U[21]*U[27] - 0.000594010608460015*U[22]*U[26] + 0.00259237963074834*U[23]*U[25] - 5.2727039845599e-6*U[24] - 2.48844888171634e-5*U[28]\n", + " U_out[25] = -5.43087366736076e-7*U[13] + 0.000529111412123567*U[21]*U[22] + 2.49364240037345e-5*U[21] + 0.00146975392256546*U[22]*U[23] + 0.00288071768822831*U[23]*U[24] - 1.03680315467796e-6*U[25] + 5.43087366736076e-7*U[2]\n", + " U_out[26] = -1.04390781015316e-6*U[16] + 0.00188040062849988*U[21]*U[23] + 0.00423090141412473*U[22]*U[24] + 2.49247013868922e-5*U[22] - 1.99291491029239e-6*U[26] + 1.04390781015316e-6*U[5]\n", + " U_out[27] = -0.000176149545706673*U[21]*U[22] + 0.00440373864266683*U[21]*U[24] + 2.49051881654425e-5*U[23] - 3.58443843007766e-6*U[27]\n", + " U_out[28] = -0.000938435679208949*U[21]*U[23] + 2.4877920928742e-5*U[24] - 5.80838943890229e-6*U[28]\n", + " U_out[29] = 9.12059910499408e-6*U[10]**4 + 4.64878611115251e-5*U[10]**3*U[12] + 1.67415119447806e-5*U[10]**3*U[14] + 9.29757222230502e-6*U[10]**3*U[17] + 0.000521179402000664*U[10] + 0.000664115245473612*U[12] + 0.000239165516521407*U[14] + 0.000132823049094722*U[17] + 1.50119582264922*U[21]*U[35] - 0.750597911324609*U[21]*U[37] - 1.50119582263017*U[22]*U[34] + 1.12589686698056*U[22]*U[36] - 4.50358746792226*U[23]*U[35] + 2.25179373396113*U[23]*U[37] - 2.62709268963613*U[24]*U[34] - 5.62948433490282*U[24]*U[36] + 1.50119582263017*U[25]*U[31] + 2.62709268963613*U[25]*U[33] - 1.50119582264922*U[26]*U[30] + 4.50358746792226*U[26]*U[32] - 1.12589686698056*U[27]*U[31] + 5.62948433490282*U[27]*U[33] + 0.750597911324609*U[28]*U[30] - 2.25179373396113*U[28]*U[32] - 8.14339205803043e-7*U[29]**4 - 1.05879118406788e-22*U[29]**3*U[30] + 4.2351647362715e-22*U[29]**3*U[32] - 0.000260589700996678*U[29] + 5.70773716830428e-5\n", + " U_out[30] = 3.3881317890172e-21*U[10]**4 - 5.31652508387687e-5*U[10]**3*U[12] - 2.75103051082387e-5*U[10]**3*U[14] - 1.06330501677537e-5*U[10]**3*U[17] - 1.06685493772574e-16*U[10] - 0.000759506950146691*U[12] - 0.000393006100803128*U[14] - 0.000151901390029338*U[17] - 1.21682350103801*U[21]*U[35] + 0.608411750519007*U[21]*U[37] + 2.34182350103837*U[22]*U[34] - 1.28761762577863*U[22]*U[36] + 5.15047050311451*U[23]*U[35] - 2.57523525155726*U[23]*U[37] + 2.12944112681652*U[24]*U[34] + 6.43808812889314*U[24]*U[36] - 2.34182350103837*U[25]*U[31] - 2.12944112681652*U[25]*U[33] + 1.21682350103801*U[26]*U[30] - 5.15047050311451*U[26]*U[32] + 1.28761762577863*U[27]*U[31] - 6.43808812889314*U[27]*U[33] - 0.608411750519007*U[28]*U[30] + 2.57523525155726*U[28]*U[32] + 1.32348898008484e-22*U[29]**4 - 3.25735682321217e-6*U[29]**3*U[30] - 8.470329472543e-22*U[29]**3*U[32] - 0.000260589700996678*U[30] - 1.16551733542192e-17\n", + " U_out[31] = 2.78802933171573e-5*U[10]**3*U[11] - 1.54836099362061e-5*U[10]**3*U[15] - 3.09672198724122e-6*U[10]**3*U[19] + 0.000398291670074584*U[11] - 0.000221195408173121*U[15] - 4.42390816346243e-5*U[19] - 1.125*U[21]*U[34] + 0.375*U[21]*U[36] + 1.875*U[23]*U[34] + 2.25*U[24]*U[35] + 1.125*U[25]*U[30] - 1.875*U[25]*U[32] - 2.25*U[26]*U[33] - 0.375*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[31] - 0.000260589700996678*U[31] + 4.36192251724445e-5\n", + " U_out[32] = -8.470329472543e-22*U[10]**4 - 1.25605469675209e-5*U[10]**3*U[12] + 2.05688778355549e-5*U[10]**3*U[14] - 2.51210939350418e-6*U[10]**3*U[17] - 5.42101086242752e-20*U[10] - 0.000179437180657833*U[12] + 0.000293842414478535*U[14] - 3.58874361315667e-5*U[17] - 1.90560783367934*U[21]*U[35] + 0.952803916839669*U[21]*U[37] - 1.46939216632066*U[22]*U[34] - 0.304205875259503*U[22]*U[36] + 1.21682350103801*U[23]*U[35] - 0.608411750519007*U[23]*U[37] + 3.33481370893884*U[24]*U[34] + 1.52102937629752*U[24]*U[36] + 1.46939216632066*U[25]*U[31] - 3.33481370893884*U[25]*U[33] + 1.90560783367934*U[26]*U[30] - 1.21682350103801*U[26]*U[32] + 0.304205875259503*U[27]*U[31] - 1.52102937629752*U[27]*U[33] - 0.952803916839669*U[28]*U[30] + 0.608411750519007*U[28]*U[32] + 1.05879118406788e-22*U[29]**4 - 4.2351647362715e-22*U[29]**3*U[30] - 3.25735682321217e-6*U[29]**3*U[32] - 0.000260589700996678*U[32]\n", + " U_out[33] = 1.11521173268629e-5*U[10]**3*U[11] + 0.000159316668029834*U[11] - 1.875*U[21]*U[36] - 2.25*U[22]*U[35] - 2.625*U[23]*U[34] + 2.625*U[25]*U[32] + 2.25*U[26]*U[31] + 1.875*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[33] - 0.000260589700996678*U[33] + 1.74476900689778e-5\n", + " U_out[34] = 3.64823964199763e-5*U[10]**3*U[13] + 0.000521179401993355*U[13] + 1.125*U[21]*U[31] - 1.125*U[22]*U[30] + 1.875*U[22]*U[32] - 1.875*U[23]*U[31] + 2.625*U[23]*U[33] - 2.625*U[24]*U[32] - 3.25735682321217e-6*U[29]**3*U[34] - 0.000260589700996678*U[34]\n", + " U_out[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", + " U_out[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", + " U_out[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", " return U_out" ] }, @@ -357,14 +432,12 @@ { "cell_type": "code", "execution_count": null, - "id": "3297ea79", + "id": "d8935949", "metadata": {}, "outputs": [], "source": [ - "%%time\n", - "ic = np.random.rand(model_parameters.ndim)*0.1\n", - "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=0)\n", - "time, ic = integrator.get_trajectories()" + "integrator_num = RungeKuttaIntegrator()\n", + "integrator_num.set_func(f_num)" ] }, { @@ -396,10 +469,22 @@ "outputs": [], "source": [ "%%time\n", - "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", + "integrator.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", "reference_time, reference_traj = integrator.get_trajectories()" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1d3ca4e", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator_num.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", + "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" + ] + }, { "cell_type": "code", "execution_count": null, @@ -412,17 +497,48 @@ "plt.figure(figsize=(10, 8))\n", "\n", "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", + "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", "\n", "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ce3d705", + "metadata": {}, + "outputs": [], + "source": [ + "reference_traj.shape" + ] + }, { "cell_type": "code", "execution_count": null, "id": "45fc0093", "metadata": {}, "outputs": [], + "source": [ + "error = reference_traj - reference_traj_num" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5e3b5782", + "metadata": {}, + "outputs": [], + "source": [ + "error[:, 1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "54b31bdb", + "metadata": {}, + "outputs": [], "source": [] } ], diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index b5e1533..d753f6b 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -930,7 +930,10 @@ def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): try: output_val = float(v) except: - output_val = v + try: + output_val = v.simplify().evalf() + except: + output_val = v print(str(ix) + ": " + str(output_val)) class SymbolicTensorDynamicT(SymbolicTensorLinear): From e1a4ad528e08df2f1a35c1bb96846130df62064e Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 9 Aug 2023 13:01:31 +0200 Subject: [PATCH 036/143] Working version of variable substitution --- qgs/functions/symbolic_output.py | 61 +++++++++++++++++--------------- qgs/tensors/symbolic_qgtensor.py | 33 +++++++++++++---- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index d0572fd..3577b05 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -143,7 +143,7 @@ def translate_equations(equations, language='python'): return str_eq -def print_equations(equations, params, save_loc=None, language='python', subs=None, return_equations=False): +def print_equations(equations, params, save_loc=None, language='python', variables=True, remain_variables=dict(), return_equations=False): ''' Function prints the equations as strings, in the programming language specified, and saves the equations to the specified location. The variables in the equation are substituted if the model variable is input. @@ -158,25 +158,31 @@ def print_equations(equations, params, save_loc=None, language='python', subs=No language: String, programming language to output the strings as - subs: String, sympy.Symbol, or list of Strings, or Bool - - substitutes the variable with the corrisponding value in the qgs model parameters + variables: Set or list of Strings, dict of sympy.Symbol with corrisponding values, or Bool + + If a set or list of strings is input, the corrisponding value in the parameters is found - if subs is True, all variables are substituted + if a dict of sympy.Symbols is input, this is used to substitute the tensor + + if True is passed, the parameters are used to substitute all variables + + remain_variables: Set or list of strings + A list or set of variables not to substitute. Only is used when variables is set to True. + ''' if equations is not None: equation_dict = dict() - if subs is not None: - # make a dictionary of variables to substitute + if variables is not None: + # make a dictionary of variables to substitute from parameters sub_vals = dict() - if subs == True: - for v in params.symbol_to_value.values(): - sub_vals[v[0]] = v[1] + if variables == True: + for key in params.symbol_to_value.keys(): + if key not in remain_variables: + sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] else: - - if isinstance(subs, list): - for s in subs: + if isinstance(variables, (set, list, dict)): + for s in variables: if isinstance(s, str): temp_sym, val = params.symbol_to_value[s] elif isinstance(s, sy.Symbol): @@ -186,19 +192,12 @@ def print_equations(equations, params, save_loc=None, language='python', subs=No raise ValueError("Incorrect type for substitution, needs to be string or sympy.Symbol, not: " + str(type(s))) sub_vals[temp_sym] = val else: - # assuming s is a sympy.Symbol or string - if isinstance(subs, str): - sub_vals[subs] = params.symbol_to_value[subs][1] - elif isinstance(subs, sy.Symbol): - val = params.symbol_to_value[subs] - sub_vals[subs] = val - else: - raise ValueError("Incorrect type for substitution, needs to be string or sympy.Symbol, not: " + str(type(subs))) + raise ValueError("Incorrect type for substitution, needs to be list, set, or dictionary, not: " + str(type(variables))) for k in equations.keys(): eq = equations[k] - if subs is not None: + if sub_vals is not None: eq = eq.subs(sub_vals) eq = eq.evalf() @@ -223,7 +222,7 @@ def print_equations(equations, params, save_loc=None, language='python', subs=No f.write("%s\n" % eq) print("Equations written") -def equation_as_function(equations, params, string_output=False, language='python', subs=None): +def equation_as_function(equations, params, string_output=False, language='python', variables=True, remain_variables=dict()): vector_subs = dict() if language == 'python': @@ -247,19 +246,25 @@ def equation_as_function(equations, params, string_output=False, language='pytho for k in equations.keys(): subed_eq[k] = equations[k].subs(vector_subs) - # determin which variables are still present in the equations - # vars = subed_eq.free_variables - - eq_list = print_equations(subed_eq, params, language=language, subs=subs, return_equations=True) + eq_list = print_equations(subed_eq, params, language=language, variables=variables, remain_variables=remain_variables, return_equations=True) + # Calculate the free variables + free_vars = set() + for func in eq_list.values(): + for vars in func.free_symbols: + if vars not in vector_subs.values(): + free_vars.add(vars) + if language == 'python': if string_output: - # eq_list = translate_equations(eq_list, language='python') f_output = list() f_output.append('def f(t, U, **kwargs):') f_output.append('\t#Tendency function of the qgs model') f_output.append('\tU_out = np.empty_like(U)') + for v in free_vars: + f_output.append('\t' + str(v) + " = kwargs['" + str(v) + "']") + for n, eq in enumerate(eq_list.values()): f_output.append('\tU_out['+str(n)+'] = ' + str(eq)) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index d753f6b..e25da7c 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -816,18 +816,37 @@ def save_to_file(self, filename, **kwargs): pickle.dump(self.__dict__, f, **kwargs) f.close() - def subs_tensor(self, tensor=None, dict_opp=True): + def subs_tensor(self, tensor=None, dict_opp=True, variables=None, remain_variables=dict()): """ Uses sympy substitution to convert the symbolic tensor or a symbolic dictionary to a numerical one. + + Parameters + ---------- + tensor: dict, sympy array + + dict_opp: Boolian, if True, uses the stored tensor_dic object as the input + + variables: dict of variable names to substitute + if None, all variables are substituted + + remain_variables: dict of variables not to substitute + if None all variables are substituted. This variable is the opposite of 'variables' """ symbol_to_number_map = list() + variables_not_found = list() - for key in self.sym_params.keys(): - try: - symbol_to_number_map.append(self.params.symbol_to_value[key]) - except: - pass + if variables is None: + for key in self.sym_params.keys(): + if key not in remain_variables: + try: + symbol_to_number_map.append(self.params.symbol_to_value[key]) + except: + variables_not_found.append(key) + + if len(variables_not_found) > 0: + print("The following variables were not found in the parameters: ") + print(variables_not_found) if isinstance(tensor, dict): ten_out = dict() @@ -855,7 +874,7 @@ def subs_tensor(self, tensor=None, dict_opp=True): return ten_out - def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-14): + def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): """ Uses sympy substitution to convert the symbolic tensor, or symbolic dictionary, to a numerical one. This is then compared to the tensor calculated by the qgs.tensor.symbolic module. From c4133a601a1c8fef6e55a7810ef7b9f9aea768ba Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 9 Aug 2023 15:18:59 +0200 Subject: [PATCH 037/143] Lambified function output working for multiple variables --- notebooks/Symbolic Output-Linear.ipynb | 138 +++++++++++++++---------- qgs/functions/symbolic_output.py | 6 +- 2 files changed, 91 insertions(+), 53 deletions(-) diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 407f178..1fc0e18 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -146,6 +146,16 @@ "ocn_loaded.load_from_file('temp_ocn_sym_lin.ip')" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "0165d8ea", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.print_params()" + ] + }, { "cell_type": "markdown", "id": "d43d341b", @@ -180,23 +190,11 @@ { "cell_type": "code", "execution_count": null, - "id": "b91f057b", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "ten = SymbolicTensorLinear(model_parameters, atm_loaded, ocn_loaded)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2b9c80ec", + "id": "c758a21b", "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(ocn_loaded._C)" + "fe = print_equations(funcs, model_parameters, return_equations=True)" ] }, { @@ -207,7 +205,7 @@ "outputs": [], "source": [ "%%time\n", - "fx = equation_as_function(funcs, model_parameters, subs=True, language='python', string_output=True)" + "fx = equation_as_function(funcs, model_parameters, remain_variables={'eps', 'n'}, language='python', string_output=False)" ] }, { @@ -223,6 +221,26 @@ " print(i)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "005608b6", + "metadata": {}, + "outputs": [], + "source": [ + "np.array(list(fe.values()))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77b88c4f", + "metadata": {}, + "outputs": [], + "source": [ + "x =np.random.rand(36)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -233,6 +251,18 @@ "from numba import njit" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "91fda04c", + "metadata": {}, + "outputs": [], + "source": [ + "@njit\n", + "def f(U):\n", + " return fx(U, n=1.5, epsilon=0.76)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -244,42 +274,44 @@ "def f(t, U):\n", "\t#Tendency function of the qgs model\n", "\tU_out = np.empty_like(U)\n", - "\tU_out[0] = -0.05*U[0] + 0.05*U[10] + 0.168986428105931*U[21] + 0.251005505082615*U[23]\n", - "\tU_out[1] = -0.980418808722262*U[0]*U[2] - 0.980418808722262*U[10]*U[12] + 0.05*U[11] - 1.56867009395562*U[13]*U[15] - 1.50055762081784*U[14]*U[17] + 1.50055762081784*U[15]*U[16] - 0.05*U[1] - 0.0112217054051163*U[20] + 0.120745727252785*U[2] - 1.56867009395562*U[3]*U[5] - 1.50055762081784*U[4]*U[7] + 1.50055762081784*U[5]*U[6]\n", - "\tU_out[2] = 0.980418808722262*U[0]*U[1] + 0.980418808722262*U[10]*U[11] + 0.05*U[12] + 1.56867009395562*U[13]*U[14] + 1.50055762081784*U[14]*U[16] + 1.50055762081784*U[15]*U[17] - 0.120745727252785*U[1] + 0.05*U[24] - 0.05*U[2] + 1.56867009395562*U[3]*U[4] + 1.50055762081784*U[4]*U[6] + 1.50055762081784*U[5]*U[7]\n", - "\tU_out[3] = 1.87265793760678*U[11]*U[15] - 1.87265793760678*U[12]*U[14] + 0.05*U[13] + 3.74531587521356*U[16]*U[19] - 3.74531587521356*U[17]*U[18] + 1.87265793760678*U[1]*U[5] - 0.00679432430697251*U[20] + 0.0810088839426413*U[22] - 1.87265793760678*U[2]*U[4] - 0.05*U[3] + 3.74531587521356*U[6]*U[9] - 3.74531587521356*U[7]*U[8]\n", - "\tU_out[4] = -1.02902937637678*U[0]*U[5] - 1.02902937637678*U[10]*U[15] + 1.73752196836555*U[11]*U[17] + 0.574852231579351*U[12]*U[13] - 1.73752196836555*U[12]*U[16] + 0.05*U[14] + 1.73752196836555*U[1]*U[7] - 0.0164935614721478*U[21] + 0.574852231579351*U[2]*U[3] - 1.73752196836555*U[2]*U[6] - 0.05*U[4] + 0.0570836566449895*U[5]\n", - "\tU_out[5] = 1.02902937637678*U[0]*U[4] + 1.02902937637678*U[10]*U[14] - 0.574852231579351*U[11]*U[13] - 1.73752196836555*U[11]*U[16] - 1.73752196836555*U[12]*U[17] + 0.05*U[15] - 0.574852231579351*U[1]*U[3] - 1.73752196836555*U[1]*U[6] + 0.05*U[25] - 1.73752196836555*U[2]*U[7] - 0.0570836566449895*U[4] - 0.05*U[5]\n", - "\tU_out[6] = -2.71889339738442*U[0]*U[7] - 2.71889339738442*U[10]*U[17] + 0.753865979381443*U[11]*U[15] + 0.753865979381443*U[12]*U[14] - 4.35022943581506*U[13]*U[19] + 0.05*U[16] + 0.753865979381443*U[1]*U[5] - 0.000777999678859866*U[20] + 0.753865979381443*U[2]*U[4] - 4.35022943581506*U[3]*U[9] - 0.05*U[6] + 0.0837128882242243*U[7]\n", - "\tU_out[7] = 2.71889339738442*U[0]*U[6] + 2.71889339738442*U[10]*U[16] - 0.753865979381443*U[11]*U[14] + 0.753865979381443*U[12]*U[15] + 4.35022943581506*U[13]*U[18] + 0.05*U[17] - 0.753865979381443*U[1]*U[4] + 0.753865979381443*U[2]*U[5] + 4.35022943581506*U[3]*U[8] - 0.0837128882242243*U[6] - 0.05*U[7]\n", - "\tU_out[8] = -2.26482546109569*U[0]*U[9] - 2.26482546109569*U[10]*U[19] - 1.7450294536311*U[13]*U[17] + 0.05*U[18] - 0.0017443933973331*U[21] - 1.7450294536311*U[3]*U[7] - 0.05*U[8] + 0.0603728636263923*U[9]\n", - "\tU_out[9] = 2.26482546109569*U[0]*U[8] + 2.26482546109569*U[10]*U[18] + 1.7450294536311*U[13]*U[16] + 0.05*U[19] + 1.7450294536311*U[3]*U[6] - 0.0603728636263923*U[8] - 0.05*U[9]\n", - "\tU_out[10] = 0.00454545454545455*U[0] - 0.0299589840451022*U[10] - 1.41868025576271*U[11]*U[2] + 1.41868025576271*U[12]*U[1] - 1.13494420461017*U[14]*U[5] + 1.13494420461017*U[15]*U[4] - 2.83736051152543*U[16]*U[7] + 2.83736051152543*U[17]*U[6] - 2.26988840922034*U[18]*U[9] + 2.26988840922034*U[19]*U[8] - 0.0153624025550847*U[21] - 0.0228186822802377*U[23] + 0.00807504401792934*U[29] + 0.00323001760717173*U[31] + 0.000468670500616653\n", - "\tU_out[11] = -1.43757363347933*U[0]*U[12] + 1.02191932371371*U[10]*U[2] - 0.0352914755316095*U[11] + 0.0255954299692664*U[12] + 1.63507091794193*U[13]*U[5] + 1.21855791962175*U[14]*U[7] - 2.30011781356693*U[15]*U[3] - 1.21855791962175*U[15]*U[6] + 1.85472813238771*U[16]*U[5] - 1.85472813238771*U[17]*U[4] + 0.0105988967691095*U[1] + 0.00237875394324372*U[20] - 0.00388732494300134*U[28]\n", - "\tU_out[12] = 1.43757363347933*U[0]*U[11] - 1.02191932371371*U[10]*U[1] - 0.0255954299692664*U[11] - 0.0352914755316095*U[12] - 1.63507091794193*U[13]*U[4] + 2.30011781356693*U[14]*U[3] - 1.21855791962175*U[14]*U[6] - 1.21855791962175*U[15]*U[7] + 1.85472813238771*U[16]*U[4] + 1.85472813238771*U[17]*U[5] - 0.0105988967691095*U[24] + 0.0105988967691095*U[2] + 0.00915929361228703*U[32]\n", - "\tU_out[13] = -1.24843862507119*U[11]*U[5] + 1.24843862507119*U[12]*U[4] - 0.0385392017497231*U[13] - 2.3185288751322*U[14]*U[2] + 2.3185288751322*U[15]*U[1] - 2.49687725014237*U[16]*U[9] + 2.49687725014237*U[17]*U[8] - 4.63705775026441*U[18]*U[7] + 4.63705775026441*U[19]*U[6] + 0.00194123551627786*U[20] - 0.0231453954121832*U[22] - 0.00317233872132938*U[28] + 0.00571020969839289*U[30] + 0.0142857142857143*U[3]\n", - "\tU_out[14] = -1.16886956037576*U[0]*U[15] + 0.422511733532696*U[10]*U[5] - 0.612715105162524*U[11]*U[7] - 1.38291034440645*U[12]*U[3] + 0.612715105162524*U[12]*U[6] + 1.79985224341047*U[13]*U[2] - 0.0419279046842654*U[14] + 0.0207014663040147*U[15] - 1.87294455066922*U[16]*U[2] + 1.87294455066922*U[17]*U[1] + 0.00598141266899433*U[21] - 0.00314405057531466*U[29] + 0.0181325685149777*U[4]\n", - "\tU_out[15] = 1.16886956037576*U[0]*U[14] - 0.422511733532696*U[10]*U[4] + 1.38291034440645*U[11]*U[3] + 0.612715105162524*U[11]*U[6] + 0.612715105162524*U[12]*U[7] - 1.79985224341047*U[13]*U[1] - 0.0207014663040147*U[14] - 0.0419279046842654*U[15] - 1.87294455066922*U[16]*U[1] - 1.87294455066922*U[17]*U[2] - 0.0181325685149777*U[25] + 0.00740799464244248*U[33] + 0.0181325685149777*U[5]\n", - "\tU_out[16] = -2.94535914360826*U[0]*U[17] + 0.569389237785845*U[10]*U[7] - 0.768581081081081*U[11]*U[5] - 0.768581081081081*U[12]*U[4] + 0.911022780457352*U[13]*U[9] + 1.42736486486486*U[14]*U[2] + 1.42736486486486*U[15]*U[1] - 0.0451998211991061*U[16] + 0.0365772529628368*U[17] - 4.71257462977322*U[19]*U[3] + 0.000339936796618951*U[20] - 0.000555519746922151*U[28] + 0.0218468468468468*U[6]\n", - "\tU_out[17] = 2.94535914360826*U[0]*U[16] - 0.569389237785845*U[10]*U[6] + 0.768581081081081*U[11]*U[4] - 0.768581081081081*U[12]*U[5] - 0.911022780457352*U[13]*U[8] - 1.42736486486486*U[14]*U[1] + 1.42736486486486*U[15]*U[2] - 0.0365772529628368*U[16] - 0.0451998211991061*U[17] + 4.71257462977322*U[18]*U[3] + 0.0218468468468468*U[7]\n", - "\tU_out[18] = -2.37660377951895*U[0]*U[19] + 0.0288656329496225*U[10]*U[9] + 1.50101291338039*U[13]*U[7] - 3.30992591155675*U[17]*U[3] - 0.0487836620662873*U[18] + 0.031291522765895*U[19] + 0.000904126828290182*U[21] - 0.000475242326846696*U[29] + 0.0259152215799615*U[8]\n", - "\tU_out[19] = 2.37660377951895*U[0]*U[18] - 0.0288656329496225*U[10]*U[8] - 1.50101291338039*U[13]*U[6] + 3.30992591155675*U[16]*U[3] - 0.031291522765895*U[18] - 0.0487836620662873*U[19] + 0.0259152215799615*U[9]\n", - "\tU_out[20] = 6.35094981132412e-8*U[11] + 8.50239961075093e-8*U[13] + 3.66419111790894e-8*U[16] - 6.35094981132412e-8*U[1] - 1.58263150747531e-7*U[20] - 0.000969739516006511*U[21]*U[24] - 0.00116937805032627*U[21]*U[26] - 0.00278561057700379*U[22]*U[25] - 0.00308506837848344*U[22]*U[27] - 0.00534779391583196*U[23]*U[26] - 7.91389255433123e-5*U[24] - 8.50239961075093e-8*U[3] - 3.66419111790894e-8*U[6]\n", - "\tU_out[21] = 4.2438907259419e-8*U[0] - 4.2438907259419e-8*U[10] + 1.34106967724862e-7*U[14] + 5.07202450868017e-8*U[18] - 0.000708243330589963*U[20]*U[24] + 0.00172613939940631*U[20]*U[26] - 4.91188341087484e-7*U[21] - 0.00626988589506499*U[22]*U[24] - 0.0119940379417069*U[23]*U[25] - 7.9002862144845e-5*U[25] - 1.34106967724862e-7*U[4] - 5.07202450868017e-8*U[8]\n", - "\tU_out[22] = -1.52343526260616e-7*U[13] - 0.00317032731211854*U[20]*U[25] + 0.0060425658523595*U[20]*U[27] + 0.00160884360522711*U[21]*U[24] - 1.04352693785129e-6*U[22] - 0.0178532887343688*U[23]*U[24] - 7.87771265478973e-5*U[26] + 1.52343526260616e-7*U[3]\n", - "\tU_out[23] = 1.68596160851496e-8*U[0] - 1.68596160851496e-8*U[10] - 0.00857174744319731*U[20]*U[26] - 0.00140681174654471*U[21]*U[25] + 0.00871788567853858*U[22]*U[24] - 1.8115193420227e-6*U[23] - 7.84632552652771e-5*U[27]\n", - "\tU_out[24] = -1.49531921957748e-7*U[12] + 0.00167798163428125*U[20]*U[21] + 7.9081381622411e-5*U[20] + 0.0046610600952257*U[21]*U[22] + 0.00913567778664237*U[22]*U[23] - 2.99063843915495e-7*U[24] + 1.49531921957748e-7*U[2]\n", - "\tU_out[25] = -3.15752734584711e-7*U[15] + 0.0059559067658186*U[20]*U[22] + 0.0134007902230919*U[21]*U[23] + 7.89455158524528e-5*U[21] - 6.31505469169422e-7*U[25] + 3.15752734584711e-7*U[5]\n", - "\tU_out[26] = -0.0005567719890759*U[20]*U[21] + 0.0139192997268975*U[20]*U[23] + 7.8720107381223e-5*U[22] - 1.18304364206348e-6*U[26]\n", - "\tU_out[27] = -0.00295762797162433*U[20]*U[22] + 7.84066893926385e-5*U[23] - 1.94992690884148e-6*U[27]\n", - "\tU_out[28] = -0.000962032794821447*U[11] - 0.00086613382185597*U[13] - 0.000192406558964289*U[16] + 0.975*U[21]*U[32] - 0.325*U[21]*U[34] + 1.3*U[22]*U[33] - 0.65*U[22]*U[35] + 1.625*U[23]*U[34] - 0.975*U[24]*U[29] - 1.3*U[25]*U[30] + 0.325*U[26]*U[29] - 1.625*U[26]*U[31] + 0.65*U[27]*U[30] - 0.00122336344718992*U[28]\n", - "\tU_out[29] = 0.00173226764371194*U[10] - 0.000962032794821447*U[14] - 0.000192406558964289*U[18] - 0.975*U[20]*U[32] + 0.325*U[20]*U[34] + 1.625*U[22]*U[32] + 1.95*U[23]*U[33] + 0.975*U[24]*U[28] - 1.625*U[24]*U[30] - 1.95*U[25]*U[31] - 0.325*U[26]*U[28] - 0.00122336344718992*U[29] + 0.000137893034416115\n", - "\tU_out[30] = 0.00155904087934075*U[13] - 1.3*U[20]*U[33] + 0.65*U[20]*U[35] - 1.625*U[21]*U[32] + 2.275*U[23]*U[32] + 1.625*U[24]*U[29] - 2.275*U[24]*U[31] + 1.3*U[25]*U[28] - 0.65*U[27]*U[28] - 0.00122336344718992*U[30]\n", - "\tU_out[31] = 0.000692907057484776*U[10] - 1.625*U[20]*U[34] - 1.95*U[21]*U[33] - 2.275*U[22]*U[32] + 2.275*U[24]*U[30] + 1.95*U[25]*U[29] + 1.625*U[26]*U[28] - 0.00122336344718992*U[31] + 5.51572137664459e-5\n", - "\tU_out[32] = 0.00226673637054264*U[12] + 0.975*U[20]*U[29] - 0.975*U[21]*U[28] + 1.625*U[21]*U[30] - 1.625*U[22]*U[29] + 2.275*U[22]*U[31] - 2.275*U[23]*U[30] - 0.00122336344718992*U[32]\n", - "\tU_out[33] = 0.00226673637054264*U[15] + 1.3*U[20]*U[30] + 1.95*U[21]*U[31] - 1.3*U[22]*U[28] - 1.95*U[23]*U[29] - 0.00122336344718992*U[33]\n", - "\tU_out[34] = -0.325*U[20]*U[29] + 1.625*U[20]*U[31] + 0.325*U[21]*U[28] - 1.625*U[23]*U[28] - 0.00122336344718992*U[34]\n", - "\tU_out[35] = -0.65*U[20]*U[30] + 0.65*U[22]*U[28] - 0.00122336344718992*U[35]\n", + "\tn = 1.3\n", + "\tepsilon = 0.76\n", + "\tU_out[0] = -0.05*U[0] + 0.05*U[10] + 0.00716448960313446*U[21]*(1.33333333333333*n**2 + 21.3333333333333) + 0.00716448960313446*U[23]*(0.533333333333333*n**2 + 34.1333333333333)\n", + "\tU_out[1] = U[0]*U[2]*(-1.20042175487614*n + 1.20042175487614*n/(n**2 + 1.0)) + U[10]*U[12]*(-1.20042175487614*n + 1.20042175487614*n/(n**2 + 1.0)) + 0.05*U[11] + U[13]*U[15]*(-1.92067480780183*n*(n**2 + 4.0)/(n**2 + 1.0) + 7.68269923120731*n/(n**2 + 1.0)) + U[14]*U[17]*(1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 1.0)) + U[15]*U[16]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0)) - 0.05*U[1] - 0.0159154943091895*U[20]*(0.333333333333333*n**2 + 1.33333333333333)/(n**2 + 1.0) + 0.249850774084608*U[2]*n/(n**2 + 1.0) + U[3]*U[5]*(-1.92067480780183*n*(n**2 + 4.0)/(n**2 + 1.0) + 7.68269923120731*n/(n**2 + 1.0)) + U[4]*U[7]*(1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 1.0)) + U[5]*U[6]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0))\n", + "\tU_out[2] = U[0]*U[1]*(1.20042175487614*n - 1.20042175487614*n/(n**2 + 1.0)) + U[10]*U[11]*(1.20042175487614*n - 1.20042175487614*n/(n**2 + 1.0)) + 0.05*U[12] + U[13]*U[14]*(1.92067480780183*n*(n**2 + 4.0)/(n**2 + 1.0) - 7.68269923120731*n/(n**2 + 1.0)) + U[14]*U[16]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0)) + U[15]*U[17]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0)) - 0.249850774084608*U[1]*n/(n**2 + 1.0) - 0.05*U[24]*(-1.0*n**2 - 1.0)/(n**2 + 1.0) - 0.05*U[2] + U[3]*U[4]*(1.92067480780183*n*(n**2 + 4.0)/(n**2 + 1.0) - 7.68269923120731*n/(n**2 + 1.0)) + U[4]*U[6]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0)) + U[5]*U[7]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0))\n", + "\tU_out[3] = U[11]*U[15]*(-0.480168701950457*n*(n**2 + 1.0) + 0.480168701950457*n*(n**2 + 4.0)) + U[12]*U[14]*(0.480168701950457*n*(n**2 + 1.0) - 0.480168701950457*n*(n**2 + 4.0)) + 0.05*U[13] + U[16]*U[19]*(3.84134961560365*n*(n**2 + 1.0) - 0.960337403900913*n*(4.0*n**2 + 1.0)) + U[17]*U[18]*(-3.84134961560365*n*(n**2 + 1.0) + 0.960337403900913*n*(4.0*n**2 + 1.0)) + U[1]*U[5]*(-0.480168701950457*n*(n**2 + 1.0) + 0.480168701950457*n*(n**2 + 4.0)) - 0.00179112240078361*U[20]*(0.666666666666667*n**2 + 2.66666666666667) + 0.00179112240078361*U[22]*(1.2*n**2 + 43.2) + U[2]*U[4]*(0.480168701950457*n*(n**2 + 1.0) - 0.480168701950457*n*(n**2 + 4.0)) - 0.05*U[3] + U[6]*U[9]*(3.84134961560365*n*(n**2 + 1.0) - 0.960337403900913*n*(4.0*n**2 + 1.0)) + U[7]*U[8]*(-3.84134961560365*n*(n**2 + 1.0) + 0.960337403900913*n*(4.0*n**2 + 1.0))\n", + "\tU_out[4] = U[0]*U[5]*(-0.960337403900913*n + 0.960337403900913*n/(n**2 + 4.0)) + U[10]*U[15]*(-0.960337403900913*n + 0.960337403900913*n/(n**2 + 4.0)) + U[11]*U[17]*(-1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (6.0*n**3 + 1.5*n)/(n**2 + 4.0)) + U[12]*U[13]*(-1.92067480780183*n*(n**2 + 1.0)/(n**2 + 4.0) + 7.68269923120731*n/(n**2 + 4.0)) + U[12]*U[16]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) + 0.05*U[14] + U[1]*U[7]*(-1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (6.0*n**3 + 1.5*n)/(n**2 + 4.0)) - 0.0159154943091895*U[21]*(0.333333333333333*n**2 + 5.33333333333333)/(n**2 + 4.0) + U[2]*U[3]*(-1.92067480780183*n*(n**2 + 1.0)/(n**2 + 4.0) + 7.68269923120731*n/(n**2 + 4.0)) + U[2]*U[6]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) - 0.05*U[4] + 0.249850774084608*U[5]*n/(n**2 + 4.0)\n", + "\tU_out[5] = U[0]*U[4]*(0.960337403900913*n - 0.960337403900913*n/(n**2 + 4.0)) + U[10]*U[14]*(0.960337403900913*n - 0.960337403900913*n/(n**2 + 4.0)) + U[11]*U[13]*(1.92067480780183*n*(n**2 + 1.0)/(n**2 + 4.0) - 7.68269923120731*n/(n**2 + 4.0)) + U[11]*U[16]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) + U[12]*U[17]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) + 0.05*U[15] + U[1]*U[3]*(1.92067480780183*n*(n**2 + 1.0)/(n**2 + 4.0) - 7.68269923120731*n/(n**2 + 4.0)) + U[1]*U[6]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) - 0.05*U[25]*(-1.0*n**2 - 4.0)/(n**2 + 4.0) + U[2]*U[7]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) - 0.249850774084608*U[4]*n/(n**2 + 4.0) - 0.05*U[5]\n", + "\tU_out[6] = U[0]*U[7]*(-2.40084350975228*n + 2.40084350975228*n/(4.0*n**2 + 1.0)) + U[10]*U[17]*(-2.40084350975228*n + 2.40084350975228*n/(4.0*n**2 + 1.0)) + U[11]*U[15]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[12]*U[14]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[13]*U[19]*(-15.3653984624146*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 15.3653984624146*n/(4.0*n**2 + 1.0)) + 0.05*U[16] + U[1]*U[5]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) - 0.0159154943091895*U[20]*(0.0666666666666667*n**2 + 0.266666666666667)/(4.0*n**2 + 1.0) + U[2]*U[4]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[3]*U[9]*(-15.3653984624146*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 15.3653984624146*n/(4.0*n**2 + 1.0)) - 0.05*U[6] + 0.499701548169216*U[7]*n/(4.0*n**2 + 1.0)\n", + "\tU_out[7] = U[0]*U[6]*(2.40084350975228*n - 2.40084350975228*n/(4.0*n**2 + 1.0)) + U[10]*U[16]*(2.40084350975228*n - 2.40084350975228*n/(4.0*n**2 + 1.0)) + U[11]*U[14]*(1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) - 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[12]*U[15]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[13]*U[18]*(15.3653984624146*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) - 15.3653984624146*n/(4.0*n**2 + 1.0)) + 0.05*U[17] + U[1]*U[4]*(1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) - 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[2]*U[5]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[3]*U[8]*(15.3653984624146*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) - 15.3653984624146*n/(4.0*n**2 + 1.0)) - 0.499701548169216*U[6]*n/(4.0*n**2 + 1.0) - 0.05*U[7]\n", + "\tU_out[8] = U[0]*U[9]*(-7.68269923120731*n*(n**2 + 1.0)/(4.0*n**2 + 4.0) + 1.92067480780183*n/(4.0*n**2 + 4.0)) + U[10]*U[19]*(-7.68269923120731*n*(n**2 + 1.0)/(4.0*n**2 + 4.0) + 1.92067480780183*n/(4.0*n**2 + 4.0)) + U[13]*U[17]*(-3.84134961560365*n*(4.0*n**2 + 1.0)/(4.0*n**2 + 4.0) + 15.3653984624146*n/(4.0*n**2 + 4.0)) + 0.05*U[18] - 0.0159154943091895*U[21]*(0.0666666666666667*n**2 + 1.06666666666667)/(4.0*n**2 + 4.0) + U[3]*U[7]*(-3.84134961560365*n*(4.0*n**2 + 1.0)/(4.0*n**2 + 4.0) + 15.3653984624146*n/(4.0*n**2 + 4.0)) - 0.05*U[8] + 0.499701548169216*U[9]*n/(4.0*n**2 + 4.0)\n", + "\tU_out[9] = U[0]*U[8]*(7.68269923120731*n*(n**2 + 1.0)/(4.0*n**2 + 4.0) - 1.92067480780183*n/(4.0*n**2 + 4.0)) + U[10]*U[18]*(7.68269923120731*n*(n**2 + 1.0)/(4.0*n**2 + 4.0) - 1.92067480780183*n/(4.0*n**2 + 4.0)) + U[13]*U[16]*(3.84134961560365*n*(4.0*n**2 + 1.0)/(4.0*n**2 + 4.0) - 15.3653984624146*n/(4.0*n**2 + 4.0)) + 0.05*U[19] + U[3]*U[6]*(3.84134961560365*n*(4.0*n**2 + 1.0)/(4.0*n**2 + 4.0) - 15.3653984624146*n/(4.0*n**2 + 4.0)) - 0.499701548169216*U[8]*n/(4.0*n**2 + 4.0) - 0.05*U[9]\n", + "\tU_out[10] = 0.00454545454545455*U[0] + U[10]*(-0.00786487737843552*epsilon - 0.0239816772374912) - 1.09129250443286*U[11]*U[2]*n + 1.09129250443286*U[12]*U[1]*n - 0.873034003546285*U[14]*U[5]*n + 0.873034003546285*U[15]*U[4]*n - 2.18258500886571*U[16]*U[7]*n + 2.18258500886571*U[17]*U[6]*n - 1.74606800709257*U[18]*U[9]*n + 1.74606800709257*U[19]*U[8]*n - 0.000651317236648587*U[21]*(1.33333333333333*n**2 + 21.3333333333333) - 0.000651317236648587*U[23]*(0.533333333333333*n**2 + 34.1333333333333) + U[29]*(0.00176721436265574*epsilon + 0.00673196110231097) + U[31]*(0.000706885745062296*epsilon + 0.00269278444092439) + 0.000468670500616653\n", + "\tU_out[11] = U[0]*U[12]*(-0.240084350975228*n*(n**2 + 1.0)/(0.2*n**2 + 2.2) - 2.16075915877705*n/(0.2*n**2 + 2.2)) + U[10]*U[2]*(-0.240084350975228*n*(n**2 + 1.0)/(0.2*n**2 + 2.2) + 2.64092786072751*n/(0.2*n**2 + 2.2)) + U[11]*(-0.0173027302325581*epsilon/(0.2*n**2 + 2.2) + 0.014*(-n**2 - 1.0)/(0.2*n**2 + 2.2) - 0.0387596899224806/(0.2*n**2 + 2.2)) + 0.0499701548169216*U[12]*n/(0.2*n**2 + 2.2) + U[13]*U[5]*(-0.384134961560365*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 5.37788946184511*n/(0.2*n**2 + 2.2)) + U[14]*U[7]*(0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 3.0*n/(0.2*n**2 + 2.2) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.2)) + U[15]*U[3]*(-0.384134961560365*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 2.30480976936219*n/(0.2*n**2 + 2.2)) + U[15]*U[6]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[16]*U[5]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[17]*U[4]*(0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 3.0*n/(0.2*n**2 + 2.2) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.2)) - 0.01*U[1]*(-n**2 - 1.0)/(0.2*n**2 + 2.2) + 0.00318309886183791*U[20]*(0.333333333333333*n**2 + 1.33333333333333)/(0.2*n**2 + 2.2) + U[28]*(-0.00215916979847569*epsilon/(0.2*n**2 + 2.2) - 0.00822506165849588/(0.2*n**2 + 2.2))\n", + "\tU_out[12] = U[0]*U[11]*(0.240084350975228*n*(n**2 + 1.0)/(0.2*n**2 + 2.2) + 2.16075915877705*n/(0.2*n**2 + 2.2)) + U[10]*U[1]*(0.240084350975228*n*(n**2 + 1.0)/(0.2*n**2 + 2.2) - 2.64092786072751*n/(0.2*n**2 + 2.2)) - 0.0499701548169216*U[11]*n/(0.2*n**2 + 2.2) + U[12]*(-0.0173027302325581*epsilon/(0.2*n**2 + 2.2) + 0.014*(-n**2 - 1.0)/(0.2*n**2 + 2.2) - 0.0387596899224806/(0.2*n**2 + 2.2)) + U[13]*U[4]*(0.384134961560365*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 5.37788946184511*n/(0.2*n**2 + 2.2)) + U[14]*U[3]*(0.384134961560365*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 2.30480976936219*n/(0.2*n**2 + 2.2)) + U[14]*U[6]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[15]*U[7]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[16]*U[4]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[17]*U[5]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + 0.01*U[24]*(-1.0*n**2 - 1.0)/(0.2*n**2 + 2.2) - 0.01*U[2]*(-n**2 - 1.0)/(0.2*n**2 + 2.2) + U[32]*(0.00508742398255814*epsilon/(0.2*n**2 + 2.2) + 0.0193798449612403/(0.2*n**2 + 2.2))\n", + "\tU_out[13] = U[11]*U[5]*(-0.13719105770013*n*(n**2 + 1.0) + 0.13719105770013*n*(n**2 + 4.0) - 1.3719105770013*n) + U[12]*U[4]*(0.13719105770013*n*(n**2 + 1.0) - 0.13719105770013*n*(n**2 + 4.0) + 1.3719105770013*n) + U[13]*(-0.00617954651162791*epsilon - 0.0338427464008859) + U[14]*U[2]*(0.13719105770013*n*(n**2 + 1.0) - 0.13719105770013*n*(n**2 + 4.0) - 1.3719105770013*n) + U[15]*U[1]*(-0.13719105770013*n*(n**2 + 1.0) + 0.13719105770013*n*(n**2 + 4.0) + 1.3719105770013*n) + U[16]*U[9]*(1.09752846160104*n*(n**2 + 1.0) - 0.274382115400261*n*(4.0*n**2 + 1.0) - 2.74382115400261*n) + U[17]*U[8]*(-1.09752846160104*n*(n**2 + 1.0) + 0.274382115400261*n*(4.0*n**2 + 1.0) + 2.74382115400261*n) + U[18]*U[7]*(-1.09752846160104*n*(n**2 + 1.0) + 0.274382115400261*n*(4.0*n**2 + 1.0) - 2.74382115400261*n) + U[19]*U[6]*(1.09752846160104*n*(n**2 + 1.0) - 0.274382115400261*n*(4.0*n**2 + 1.0) + 2.74382115400261*n) + 0.000511749257366747*U[20]*(0.666666666666667*n**2 + 2.66666666666667) - 0.000511749257366747*U[22]*(1.2*n**2 + 43.2) + U[28]*(-0.000694262785329041*epsilon - 0.00264469900447931) + U[30]*(0.00124967301359227*epsilon + 0.00476045820806276) + 0.0142857142857143*U[3]\n", + "\tU_out[14] = U[0]*U[15]*(-0.192067480780183*n*(n**2 + 4.0)/(0.2*n**2 + 2.8) - 1.72860732702164*n/(0.2*n**2 + 2.8)) + U[10]*U[5]*(-0.192067480780183*n*(n**2 + 4.0)/(0.2*n**2 + 2.8) + 2.11274228858201*n/(0.2*n**2 + 2.8)) + U[11]*U[7]*(-0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 3.0*n/(0.2*n**2 + 2.8) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.8)) + U[12]*U[3]*(-0.384134961560365*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 2.30480976936219*n/(0.2*n**2 + 2.8)) + U[12]*U[6]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[13]*U[2]*(-0.384134961560365*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 5.37788946184511*n/(0.2*n**2 + 2.8)) + U[14]*(-0.0173027302325581*epsilon/(0.2*n**2 + 2.8) + 0.014*(-n**2 - 4.0)/(0.2*n**2 + 2.8) - 0.0387596899224806/(0.2*n**2 + 2.8)) + 0.0499701548169216*U[15]*n/(0.2*n**2 + 2.8) + U[16]*U[2]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[17]*U[1]*(-0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 3.0*n/(0.2*n**2 + 2.8) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.8)) + 0.00318309886183791*U[21]*(0.333333333333333*n**2 + 5.33333333333333)/(0.2*n**2 + 2.8) + U[29]*(-0.00215916979847569*epsilon/(0.2*n**2 + 2.8) - 0.00822506165849588/(0.2*n**2 + 2.8)) - 0.01*U[4]*(-n**2 - 4.0)/(0.2*n**2 + 2.8)\n", + "\tU_out[15] = U[0]*U[14]*(0.192067480780183*n*(n**2 + 4.0)/(0.2*n**2 + 2.8) + 1.72860732702164*n/(0.2*n**2 + 2.8)) + U[10]*U[4]*(0.192067480780183*n*(n**2 + 4.0)/(0.2*n**2 + 2.8) - 2.11274228858201*n/(0.2*n**2 + 2.8)) + U[11]*U[3]*(0.384134961560365*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 2.30480976936219*n/(0.2*n**2 + 2.8)) + U[11]*U[6]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[12]*U[7]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[13]*U[1]*(0.384134961560365*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 5.37788946184511*n/(0.2*n**2 + 2.8)) - 0.0499701548169216*U[14]*n/(0.2*n**2 + 2.8) + U[15]*(-0.0173027302325581*epsilon/(0.2*n**2 + 2.8) + 0.014*(-n**2 - 4.0)/(0.2*n**2 + 2.8) - 0.0387596899224806/(0.2*n**2 + 2.8)) + U[16]*U[1]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[17]*U[2]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + 0.01*U[25]*(-1.0*n**2 - 4.0)/(0.2*n**2 + 2.8) + U[33]*(0.00508742398255814*epsilon/(0.2*n**2 + 2.8) + 0.0193798449612403/(0.2*n**2 + 2.8)) - 0.01*U[5]*(-n**2 - 4.0)/(0.2*n**2 + 2.8)\n", + "\tU_out[16] = U[0]*U[17]*(-0.480168701950457*n*(4.0*n**2 + 1.0)/(0.8*n**2 + 2.2) - 4.32151831755411*n/(0.8*n**2 + 2.2)) + U[10]*U[7]*(-0.480168701950457*n*(4.0*n**2 + 1.0)/(0.8*n**2 + 2.2) + 5.28185572145502*n/(0.8*n**2 + 2.2)) + U[11]*U[5]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) - 3.0*n/(0.8*n**2 + 2.2)) + U[12]*U[4]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) - 3.0*n/(0.8*n**2 + 2.2)) + U[13]*U[9]*(-3.07307969248292*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 10.7557789236902*n/(0.8*n**2 + 2.2)) + U[14]*U[2]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) + 3.0*n/(0.8*n**2 + 2.2)) + U[15]*U[1]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) + 3.0*n/(0.8*n**2 + 2.2)) + U[16]*(-0.0173027302325581*epsilon/(0.8*n**2 + 2.2) + 0.014*(-4.0*n**2 - 1.0)/(0.8*n**2 + 2.2) - 0.0387596899224806/(0.8*n**2 + 2.2)) + 0.0999403096338432*U[17]*n/(0.8*n**2 + 2.2) + U[19]*U[3]*(-3.07307969248292*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) - 4.60961953872438*n/(0.8*n**2 + 2.2)) + 0.00318309886183791*U[20]*(0.0666666666666667*n**2 + 0.266666666666667)/(0.8*n**2 + 2.2) + U[28]*(-0.000431833959695138*epsilon/(0.8*n**2 + 2.2) - 0.00164501233169918/(0.8*n**2 + 2.2)) - 0.01*U[6]*(-4.0*n**2 - 1.0)/(0.8*n**2 + 2.2)\n", + "\tU_out[17] = U[0]*U[16]*(0.480168701950457*n*(4.0*n**2 + 1.0)/(0.8*n**2 + 2.2) + 4.32151831755411*n/(0.8*n**2 + 2.2)) + U[10]*U[6]*(0.480168701950457*n*(4.0*n**2 + 1.0)/(0.8*n**2 + 2.2) - 5.28185572145502*n/(0.8*n**2 + 2.2)) + U[11]*U[4]*(0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) - 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) + 3.0*n/(0.8*n**2 + 2.2)) + U[12]*U[5]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) - 3.0*n/(0.8*n**2 + 2.2)) + U[13]*U[8]*(3.07307969248292*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) - 10.7557789236902*n/(0.8*n**2 + 2.2)) + U[14]*U[1]*(0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) - 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) - 3.0*n/(0.8*n**2 + 2.2)) + U[15]*U[2]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) + 3.0*n/(0.8*n**2 + 2.2)) - 0.0999403096338432*U[16]*n/(0.8*n**2 + 2.2) + U[17]*(-0.0173027302325581*epsilon/(0.8*n**2 + 2.2) + 0.014*(-4.0*n**2 - 1.0)/(0.8*n**2 + 2.2) - 0.0387596899224806/(0.8*n**2 + 2.2)) + U[18]*U[3]*(3.07307969248292*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 4.60961953872438*n/(0.8*n**2 + 2.2)) - 0.01*U[7]*(-4.0*n**2 - 1.0)/(0.8*n**2 + 2.2)\n", + "\tU_out[18] = U[0]*U[19]*(-0.768269923120731*n*(n**2 + 1.0)/(0.4*n**2 + 1.4) - 1.72860732702164*n/(0.4*n**2 + 1.4)) + U[10]*U[9]*(-0.768269923120731*n*(n**2 + 1.0)/(0.4*n**2 + 1.4) + 2.11274228858201*n/(0.4*n**2 + 1.4)) + U[13]*U[7]*(-0.384134961560365*n*(4.0*n**2 + 1.0)/(0.4*n**2 + 1.4) + 5.37788946184511*n/(0.4*n**2 + 1.4)) + U[17]*U[3]*(-0.384134961560365*n*(4.0*n**2 + 1.0)/(0.4*n**2 + 1.4) - 2.30480976936219*n/(0.4*n**2 + 1.4)) + U[18]*(-0.00865136511627907*epsilon/(0.4*n**2 + 1.4) + 0.007*(-4.0*n**2 - 4.0)/(0.4*n**2 + 1.4) - 0.0193798449612403/(0.4*n**2 + 1.4)) + 0.0499701548169216*U[19]*n/(0.4*n**2 + 1.4) + 0.00159154943091895*U[21]*(0.0666666666666667*n**2 + 1.06666666666667)/(0.4*n**2 + 1.4) + U[29]*(-0.000215916979847569*epsilon/(0.4*n**2 + 1.4) - 0.000822506165849588/(0.4*n**2 + 1.4)) - 0.005*U[8]*(-4.0*n**2 - 4.0)/(0.4*n**2 + 1.4)\n", + "\tU_out[19] = U[0]*U[18]*(0.768269923120731*n*(n**2 + 1.0)/(0.4*n**2 + 1.4) + 1.72860732702164*n/(0.4*n**2 + 1.4)) + U[10]*U[8]*(0.768269923120731*n*(n**2 + 1.0)/(0.4*n**2 + 1.4) - 2.11274228858201*n/(0.4*n**2 + 1.4)) + U[13]*U[6]*(0.384134961560365*n*(4.0*n**2 + 1.0)/(0.4*n**2 + 1.4) - 5.37788946184511*n/(0.4*n**2 + 1.4)) + U[16]*U[3]*(0.384134961560365*n*(4.0*n**2 + 1.0)/(0.4*n**2 + 1.4) + 2.30480976936219*n/(0.4*n**2 + 1.4)) - 0.0499701548169216*U[18]*n/(0.4*n**2 + 1.4) + U[19]*(-0.00865136511627907*epsilon/(0.4*n**2 + 1.4) + 0.007*(-4.0*n**2 - 4.0)/(0.4*n**2 + 1.4) - 0.0193798449612403/(0.4*n**2 + 1.4)) - 0.005*U[9]*(-4.0*n**2 - 4.0)/(0.4*n**2 + 1.4)\n", + "\tU_out[20] = 0.000637442278533431*U[11]*(n**2 + 1.0)/(3.875*n**2 + 26992.8730718743) + 0.00229559873588804*U[13]/(3.875*n**2 + 26992.8730718743) + 0.000478081708900073*U[16]*(1.06666666666667*n**2 + 0.266666666666667)/(3.875*n**2 + 26992.8730718743) - 0.000637442278533431*U[1]*(n**2 + 1.0)/(3.875*n**2 + 26992.8730718743) + 0.00300387596899225*U[20]*(-0.25*n**2 - 1.0)/(3.875*n**2 + 26992.8730718743) + U[21]*U[24]*(-15.5*n*(0.1875*n**2 + 3.0)/(3.875*n**2 + 26992.8730718743) + 11.625*n*(n**2 + 1.0)/(3.875*n**2 + 26992.8730718743)) + U[21]*U[26]*(15.5*n*(0.0625*n**2 + 1.0)/(3.875*n**2 + 26992.8730718743) - 15.5*n*(0.25*n**2 + 2.25)/(3.875*n**2 + 26992.8730718743)) + U[22]*U[25]*(-15.5*n*(0.25*n**2 + 9.0)/(3.875*n**2 + 26992.8730718743) + 15.5*n*(1.0*n**2 + 4.0)/(3.875*n**2 + 26992.8730718743)) + U[22]*U[27]*(15.5*n*(0.125*n**2 + 4.5)/(3.875*n**2 + 26992.8730718743) - 15.5*n*(0.5*n**2 + 8.0)/(3.875*n**2 + 26992.8730718743)) + U[23]*U[26]*(-15.5*n*(0.3125*n**2 + 20.0)/(3.875*n**2 + 26992.8730718743) + 15.5*n*(1.25*n**2 + 11.25)/(3.875*n**2 + 26992.8730718743)) - 1.64361941021061*U[24]*n/(3.875*n**2 + 26992.8730718743) - 0.00229559873588804*U[3]/(3.875*n**2 + 26992.8730718743) - 0.000478081708900073*U[6]*(1.06666666666667*n**2 + 0.266666666666667)/(3.875*n**2 + 26992.8730718743)\n", + "\tU_out[21] = 0.00114779936794402*U[0]/(3.875*n**2 + 27039.3730718743) - 0.00114779936794402*U[10]/(3.875*n**2 + 27039.3730718743) + 0.000478081708900073*U[14]*(1.33333333333333*n**2 + 5.33333333333333)/(3.875*n**2 + 27039.3730718743) + 0.000509953822826745*U[18]*(n**2 + 1.0)/(3.875*n**2 + 27039.3730718743) + U[20]*U[24]*(15.5*n*(0.1875*n**2 + 0.75)/(3.875*n**2 + 27039.3730718743) - 11.625*n*(n**2 + 1.0)/(3.875*n**2 + 27039.3730718743)) + U[20]*U[26]*(-15.5*n*(0.0625*n**2 + 0.25)/(3.875*n**2 + 27039.3730718743) + 15.5*n*(0.25*n**2 + 2.25)/(3.875*n**2 + 27039.3730718743)) + 0.00300387596899225*U[21]*(-0.25*n**2 - 4.0)/(3.875*n**2 + 27039.3730718743) + U[22]*U[24]*(-15.5*n*(0.3125*n**2 + 11.25)/(3.875*n**2 + 27039.3730718743) + 19.375*n*(n**2 + 1.0)/(3.875*n**2 + 27039.3730718743)) + U[23]*U[25]*(-15.5*n*(0.375*n**2 + 24.0)/(3.875*n**2 + 27039.3730718743) + 15.5*n*(1.5*n**2 + 6.0)/(3.875*n**2 + 27039.3730718743)) - 1.64361941021061*U[25]*n/(3.875*n**2 + 27039.3730718743) - 0.000478081708900073*U[4]*(1.33333333333333*n**2 + 5.33333333333333)/(3.875*n**2 + 27039.3730718743) - 0.000509953822826745*U[8]*(n**2 + 1.0)/(3.875*n**2 + 27039.3730718743)\n", + "\tU_out[22] = -0.00413207772459848*U[13]/(3.875*n**2 + 27116.8730718743) + U[20]*U[25]*(15.5*n*(0.25*n**2 + 1.0)/(3.875*n**2 + 27116.8730718743) - 15.5*n*(1.0*n**2 + 4.0)/(3.875*n**2 + 27116.8730718743)) + U[20]*U[27]*(-15.5*n*(0.125*n**2 + 0.5)/(3.875*n**2 + 27116.8730718743) + 15.5*n*(0.5*n**2 + 8.0)/(3.875*n**2 + 27116.8730718743)) + U[21]*U[24]*(15.5*n*(0.3125*n**2 + 5.0)/(3.875*n**2 + 27116.8730718743) - 19.375*n*(n**2 + 1.0)/(3.875*n**2 + 27116.8730718743)) + 0.00300387596899225*U[22]*(-0.25*n**2 - 9.0)/(3.875*n**2 + 27116.8730718743) + U[23]*U[24]*(-15.5*n*(0.4375*n**2 + 28.0)/(3.875*n**2 + 27116.8730718743) + 27.125*n*(n**2 + 1.0)/(3.875*n**2 + 27116.8730718743)) - 1.64361941021061*U[26]*n/(3.875*n**2 + 27116.8730718743) + 0.00413207772459848*U[3]/(3.875*n**2 + 27116.8730718743)\n", + "\tU_out[23] = 0.000459119747177608*U[0]/(3.875*n**2 + 27225.3730718743) - 0.000459119747177608*U[10]/(3.875*n**2 + 27225.3730718743) + U[20]*U[26]*(15.5*n*(0.3125*n**2 + 1.25)/(3.875*n**2 + 27225.3730718743) - 15.5*n*(1.25*n**2 + 11.25)/(3.875*n**2 + 27225.3730718743)) + U[21]*U[25]*(15.5*n*(0.375*n**2 + 6.0)/(3.875*n**2 + 27225.3730718743) - 15.5*n*(1.5*n**2 + 6.0)/(3.875*n**2 + 27225.3730718743)) + U[22]*U[24]*(15.5*n*(0.4375*n**2 + 15.75)/(3.875*n**2 + 27225.3730718743) - 27.125*n*(n**2 + 1.0)/(3.875*n**2 + 27225.3730718743)) + 0.00300387596899225*U[23]*(-0.25*n**2 - 16.0)/(3.875*n**2 + 27225.3730718743) - 1.64361941021061*U[27]*n/(3.875*n**2 + 27225.3730718743)\n", + "\tU_out[24] = 0.00150193798449612*U[12]*(-1.0*n**2 - 1.0)/(15.5*n**2 + 26992.8730718743) + U[20]*U[21]*(-15.5*n*(0.1875*n**2 + 0.75)/(15.5*n**2 + 26992.8730718743) + 15.5*n*(0.1875*n**2 + 3.0)/(15.5*n**2 + 26992.8730718743)) + 1.64361941021061*U[20]*n/(15.5*n**2 + 26992.8730718743) + U[21]*U[22]*(-15.5*n*(0.3125*n**2 + 5.0)/(15.5*n**2 + 26992.8730718743) + 15.5*n*(0.3125*n**2 + 11.25)/(15.5*n**2 + 26992.8730718743)) + U[22]*U[23]*(-15.5*n*(0.4375*n**2 + 15.75)/(15.5*n**2 + 26992.8730718743) + 15.5*n*(0.4375*n**2 + 28.0)/(15.5*n**2 + 26992.8730718743)) + 0.00300387596899225*U[24]*(-1.0*n**2 - 1.0)/(15.5*n**2 + 26992.8730718743) - 0.00150193798449612*U[2]*(-1.0*n**2 - 1.0)/(15.5*n**2 + 26992.8730718743)\n", + "\tU_out[25] = 0.00150193798449612*U[15]*(-1.0*n**2 - 4.0)/(15.5*n**2 + 27039.3730718743) + U[20]*U[22]*(-15.5*n*(0.25*n**2 + 1.0)/(15.5*n**2 + 27039.3730718743) + 15.5*n*(0.25*n**2 + 9.0)/(15.5*n**2 + 27039.3730718743)) + U[21]*U[23]*(-15.5*n*(0.375*n**2 + 6.0)/(15.5*n**2 + 27039.3730718743) + 15.5*n*(0.375*n**2 + 24.0)/(15.5*n**2 + 27039.3730718743)) + 1.64361941021061*U[21]*n/(15.5*n**2 + 27039.3730718743) + 0.00300387596899225*U[25]*(-1.0*n**2 - 4.0)/(15.5*n**2 + 27039.3730718743) - 0.00150193798449612*U[5]*(-1.0*n**2 - 4.0)/(15.5*n**2 + 27039.3730718743)\n", + "\tU_out[26] = U[20]*U[21]*(15.5*n*(0.0625*n**2 + 0.25)/(15.5*n**2 + 27116.8730718743) - 15.5*n*(0.0625*n**2 + 1.0)/(15.5*n**2 + 27116.8730718743)) + U[20]*U[23]*(-15.5*n*(0.3125*n**2 + 1.25)/(15.5*n**2 + 27116.8730718743) + 15.5*n*(0.3125*n**2 + 20.0)/(15.5*n**2 + 27116.8730718743)) + 1.64361941021061*U[22]*n/(15.5*n**2 + 27116.8730718743) + 0.00300387596899225*U[26]*(-1.0*n**2 - 9.0)/(15.5*n**2 + 27116.8730718743)\n", + "\tU_out[27] = U[20]*U[22]*(15.5*n*(0.125*n**2 + 0.5)/(15.5*n**2 + 27225.3730718743) - 15.5*n*(0.125*n**2 + 4.5)/(15.5*n**2 + 27225.3730718743)) + 1.64361941021061*U[23]*n/(15.5*n**2 + 27225.3730718743) + 0.00300387596899225*U[27]*(-1.0*n**2 - 16.0)/(15.5*n**2 + 27225.3730718743)\n", + "\tU_out[28] = U[11]*(-0.000183587669699814*epsilon - 0.000822506165849588) + U[13]*(-0.000165286974476004*epsilon - 0.000740515721254207) + U[16]*(-3.67175339399628e-5*epsilon - 0.000164501233169918) + 0.75*U[21]*U[32]*n - 0.25*U[21]*U[34]*n + 1.0*U[22]*U[33]*n - 0.5*U[22]*U[35]*n + 1.25*U[23]*U[34]*n - 0.75*U[24]*U[29]*n - 1.0*U[25]*U[30]*n + 0.25*U[26]*U[29]*n - 1.25*U[26]*U[31]*n + 0.5*U[27]*U[30]*n - 0.00122336344718992*U[28]\n", + "\tU_out[29] = U[10]*(0.000330573948952008*epsilon + 0.00148103144250841) + U[14]*(-0.000183587669699814*epsilon - 0.000822506165849588) + U[18]*(-3.67175339399628e-5*epsilon - 0.000164501233169918) - 0.75*U[20]*U[32]*n + 0.25*U[20]*U[34]*n + 1.25*U[22]*U[32]*n + 1.5*U[23]*U[33]*n + 0.75*U[24]*U[28]*n - 1.25*U[24]*U[30]*n - 1.5*U[25]*U[31]*n - 0.25*U[26]*U[28]*n - 0.00122336344718992*U[29] + 0.000137893034416115\n", + "\tU_out[30] = U[13]*(0.000297516554056807*epsilon + 0.00133292829825757) - 1.0*U[20]*U[33]*n + 0.5*U[20]*U[35]*n - 1.25*U[21]*U[32]*n + 1.75*U[23]*U[32]*n + 1.25*U[24]*U[29]*n - 1.75*U[24]*U[31]*n + 1.0*U[25]*U[28]*n - 0.5*U[27]*U[28]*n - 0.00122336344718992*U[30]\n", + "\tU_out[31] = U[10]*(0.000132229579580803*epsilon + 0.000592412577003366) - 1.25*U[20]*U[34]*n - 1.5*U[21]*U[33]*n - 1.75*U[22]*U[32]*n + 1.75*U[24]*U[30]*n + 1.5*U[25]*U[29]*n + 1.25*U[26]*U[28]*n - 0.00122336344718992*U[31] + 5.51572137664459e-5\n", + "\tU_out[32] = U[12]*(0.000432568255813953*epsilon + 0.00193798449612403) + 0.75*U[20]*U[29]*n - 0.75*U[21]*U[28]*n + 1.25*U[21]*U[30]*n - 1.25*U[22]*U[29]*n + 1.75*U[22]*U[31]*n - 1.75*U[23]*U[30]*n - 0.00122336344718992*U[32]\n", + "\tU_out[33] = U[15]*(0.000432568255813953*epsilon + 0.00193798449612403) + 1.0*U[20]*U[30]*n + 1.5*U[21]*U[31]*n - 1.0*U[22]*U[28]*n - 1.5*U[23]*U[29]*n - 0.00122336344718992*U[33]\n", + "\tU_out[34] = -0.25*U[20]*U[29]*n + 1.25*U[20]*U[31]*n + 0.25*U[21]*U[28]*n - 1.25*U[23]*U[28]*n - 0.00122336344718992*U[34]\n", + "\tU_out[35] = -0.5*U[20]*U[30]*n + 0.5*U[22]*U[28]*n - 0.00122336344718992*U[35]\n", "\treturn U_out" ] }, @@ -335,6 +367,8 @@ "metadata": {}, "outputs": [], "source": [ + "# ICs calculated from long transient\n", + "\n", "ic = np.array([ 0.03099396, -0.00496147, 0.00173704, -0.00135697, -0.00772211,\n", " -0.01966693, 0.0133646 , 0.001414 , 0.01117828, -0.00041981,\n", " 0.0296539 , -0.00059846, -0.0054299 , 0.00265086, -0.00813694,\n", @@ -404,7 +438,7 @@ { "cell_type": "code", "execution_count": null, - "id": "978ac07b", + "id": "9f8ddaac", "metadata": {}, "outputs": [], "source": [] diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 3577b05..0f6d6aa 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -272,7 +272,11 @@ def equation_as_function(equations, params, string_output=False, language='pytho else: # Return a lamdafied function vec = [sy.Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] - f_output = sy.lambdify([vec], eq_list) + array_eqs = np.array(list(eq_list.values())) + inputs = [vec] + for v in free_vars: + inputs.append(v) + f_output = sy.lambdify(inputs, array_eqs) if language == 'julia': eq_list = translate_equations(eq_list, language='julia') From b69e5d524c39acd95948c94d68230a2cf267364b Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 10 Aug 2023 15:02:33 +0200 Subject: [PATCH 038/143] Trying to fix O IP --- notebooks/Symbolic Output-dynamic_T.ipynb | 197 ++++++++++------------ qgs/tensors/symbolic_qgtensor.py | 2 +- 2 files changed, 90 insertions(+), 109 deletions(-) diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index cd55b86..a9e47de 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -188,10 +188,13 @@ "cell_type": "code", "execution_count": null, "id": "9f1ffbb0", - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "ocn_num = OceanicSymbolicInnerProducts(model_parameters, stored=True)" + "ocn_num = OceanicSymbolicInnerProducts(model_parameters, stored=True)\n", + "atm_num = AtmosphericSymbolicInnerProducts(model_parameters, stored=True)" ] }, { @@ -221,7 +224,7 @@ }, "outputs": [], "source": [ - "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" + "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True, )" ] }, { @@ -249,6 +252,18 @@ "ten = SymbolicTensorDynamicT(model_parameters, atm_loaded, ocn_loaded, numerically_test_tensor=False)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "e486fb7c", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "ten.test_tensor_numerically(tol=1e-14)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -280,49 +295,25 @@ { "cell_type": "code", "execution_count": null, - "id": "7d8fe02f", - "metadata": { - "scrolled": true - }, + "id": "1a4a2519", + "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(ocn_loaded._C)" + "ten.print_tensor(ocn_loaded._O)" ] }, { "cell_type": "code", "execution_count": null, "id": "aa2d7a41", - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "ten.print_tensor(ocn_num._O.todense())" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ce94a64", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7eb27d00", - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e63015c3", - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, @@ -331,7 +322,7 @@ "outputs": [], "source": [ "%%time\n", - "fx = equation_as_function(funcs, model_parameters, subs=True, language='julia', string_output=True)" + "fx = equation_as_function(funcs[0], model_parameters, variables=True, language='python', string_output=True)" ] }, { @@ -364,47 +355,47 @@ "source": [ "@njit\n", "def f(t, U):\n", - " #Tendency function of the qgs model\n", - " U_out = np.empty_like(U)\n", - " U_out[0] = -0.0145*U[0] + 0.0145*U[11] + 0.0505574149661188*U[22] + 0.0734121368001177*U[24]\n", - " U_out[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[11]*U[13] + 0.0145*U[12] - 1.9945469157942*U[14]*U[16] - 2.59615384615385*U[15]*U[18] + 2.59615384615385*U[16]*U[17] - 0.0145*U[1] - 0.00295864958311857*U[21] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", - " U_out[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[11]*U[12] + 0.0145*U[13] + 1.9945469157942*U[14]*U[15] + 2.59615384615385*U[15]*U[17] + 2.59615384615385*U[16]*U[18] - 0.115315741885204*U[1] + 0.0145*U[25] - 0.0145*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", - " U_out[3] = 2.16075915877706*U[12]*U[16] - 2.16075915877706*U[13]*U[15] + 0.0145*U[14] + 4.32151831755411*U[17]*U[20] - 4.32151831755411*U[18]*U[19] + 2.16075915877706*U[1]*U[5] - 0.00216427290094687*U[21] + 0.0238416302768307*U[23] - 2.16075915877706*U[2]*U[4] - 0.0145*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", - " U_out[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[11]*U[16] + 2.43*U[12]*U[18] + 0.345721465404329*U[13]*U[14] - 2.43*U[13]*U[17] + 0.0145*U[15] + 2.43*U[1]*U[7] - 0.00449241352700723*U[22] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.0145*U[4] + 0.0599641857803059*U[5]\n", - " U_out[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[11]*U[15] - 0.345721465404329*U[12]*U[14] - 2.43*U[12]*U[17] - 2.43*U[13]*U[18] + 0.0145*U[16] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.0145*U[26] - 2.43*U[2]*U[7] - 0.0599641857803059*U[4] - 0.0145*U[5]\n", - " U_out[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[11]*U[18] + 0.675*U[12]*U[16] + 0.675*U[13]*U[15] - 5.18582198106493*U[14]*U[20] + 0.0145*U[17] + 0.675*U[1]*U[5] - 0.000192312222902707*U[21] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.0145*U[6] + 0.0749552322253824*U[7]\n", - " U_out[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[11]*U[17] - 0.675*U[12]*U[15] + 0.675*U[13]*U[16] + 5.18582198106493*U[14]*U[19] + 0.0145*U[18] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.0145*U[7]\n", - " U_out[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[11]*U[20] - 2.65939588772561*U[14]*U[18] + 0.0145*U[19] - 0.000431962839135311*U[22] - 2.65939588772561*U[3]*U[7] - 0.0145*U[8] + 0.0576578709426019*U[9]\n", - " U_out[9] = 2.65939588772561*U[0]*U[8] + 2.65939588772561*U[11]*U[19] + 2.65939588772561*U[14]*U[17] + 0.0145*U[20] + 2.65939588772561*U[3]*U[6] - 0.0576578709426019*U[8] - 0.0145*U[9]\n", - " U_out[10] = -0.000510753549879668*U[10]**4 - 0.014593023255814*U[10] - 0.00183644645351737*U[21] - 0.00374635076517544*U[23] + 1.59610484337396e-5*U[29]**4 + 5.17501542233338e-5*U[29]**3*U[30] + 1.72500514077779e-5*U[29]**3*U[32] + 0.00729651162790698*U[29] + 0.00591432955679692*U[30] + 0.00197144318559898*U[32] + 0.000531003677198668\n", - " U_out[11] = 0.00131818181818182*U[0] - 0.00185728563592607*U[10]**3*U[11] - 0.0198572938689218*U[11] - 1.63693875664928*U[12]*U[2] + 1.63693875664928*U[13]*U[1] - 1.30955100531943*U[15]*U[5] + 1.30955100531943*U[16]*U[4] - 3.27387751329857*U[17]*U[7] + 3.27387751329857*U[18]*U[6] - 2.61910201063885*U[19]*U[9] + 2.61910201063885*U[20]*U[8] - 0.00459612863328353*U[22] - 0.00667383061819252*U[24] + 4.43550120954776e-5*U[29]**3*U[31] + 1.7742004838191e-5*U[29]**3*U[33] + 0.00506916671004016*U[31] + 0.00202766668401607*U[33] + 0.000482730615635153\n", - " U_out[12] = -1.6647358298754*U[0]*U[13] - 0.0015418975090707*U[10]**3*U[12] + 1.05320021890077*U[11]*U[2] - 0.0287966213251426*U[12] + 0.0282849932925971*U[13] + 1.68512035024123*U[14]*U[5] + 1.06132075471698*U[15]*U[7] - 2.66357732780065*U[16]*U[3] - 1.06132075471698*U[16]*U[6] + 2.33490566037736*U[17]*U[5] - 2.33490566037736*U[18]*U[4] + 0.00355660377358491*U[1] + 0.000725706501519649*U[21] - 2.04500508591402e-5*U[29]**3*U[30] - 0.00233715902975374*U[30]\n", - " U_out[13] = 1.6647358298754*U[0]*U[12] - 0.0015418975090707*U[10]**3*U[13] - 1.05320021890077*U[11]*U[1] - 0.0282849932925971*U[12] - 0.0287966213251426*U[13] - 1.68512035024123*U[14]*U[4] + 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] - 1.06132075471698*U[16]*U[7] + 2.33490566037736*U[17]*U[4] + 2.33490566037736*U[18]*U[5] - 0.00355660377358491*U[25] + 4.81842971584593e-5*U[29]**3*U[34] + 0.00355660377358491*U[2] + 0.00550680122860904*U[34]\n", - " U_out[14] = -0.00145929585679905*U[10]**3*U[14] - 1.44050610585137*U[12]*U[5] + 1.44050610585137*U[13]*U[4] - 0.0311378737541528*U[14] - 2.67522562515254*U[15]*U[2] + 2.67522562515254*U[16]*U[1] - 2.88101221170274*U[17]*U[9] + 2.88101221170274*U[18]*U[8] - 5.35045125030509*U[19]*U[7] + 5.35045125030509*U[20]*U[6] + 0.000618363685984819*U[21] - 0.00681189436480877*U[23] - 1.74251833232233e-5*U[29]**3*U[30] + 3.1365329981802e-5*U[29]**3*U[32] - 0.00199145835037292*U[30] + 0.00358462503067126*U[32] + 0.00414285714285714*U[3]\n", - " U_out[15] = -1.35185957626052*U[0]*U[16] - 0.00125723950739611*U[10]**3*U[15] + 0.421071015556554*U[11]*U[5] - 0.45*U[12]*U[7] - 1.63996079743079*U[13]*U[3] + 0.45*U[13]*U[6] + 1.90590038620335*U[14]*U[2] - 0.0368649373881932*U[15] + 0.0230631483770407*U[16] - 2.31923076923077*U[17]*U[2] + 2.31923076923077*U[18]*U[1] + 0.00172785135654124*U[22] - 1.66746568543758e-5*U[29]**3*U[31] - 0.00190568351656843*U[31] + 0.00557692307692308*U[4]\n", - " U_out[16] = 1.35185957626052*U[0]*U[15] - 0.00125723950739611*U[10]**3*U[16] - 0.421071015556554*U[11]*U[4] + 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 0.45*U[13]*U[7] - 1.90590038620335*U[14]*U[1] - 0.0230631483770407*U[15] - 0.0368649373881932*U[16] - 2.31923076923077*U[17]*U[1] - 2.31923076923077*U[18]*U[2] - 0.00557692307692308*U[26] + 3.92887346061283e-5*U[29]**3*U[35] + 0.00449016100178891*U[35] + 0.00557692307692308*U[5]\n", - " U_out[17] = -3.421202001397*U[0]*U[18] - 0.00102150709975934*U[10]**3*U[17] + 0.180063263231421*U[11]*U[7] - 0.7875*U[12]*U[5] - 0.7875*U[13]*U[4] + 0.288101221170275*U[14]*U[9] + 1.4625*U[15]*U[2] + 1.4625*U[16]*U[1] - 0.043546511627907*U[17] + 0.0374776161126912*U[18] - 5.47392320223521*U[20]*U[3] + 9.61561114513534e-5*U[21] - 2.70963173883607e-6*U[29]**3*U[30] - 0.00030967357144237*U[30] + 0.00725*U[6]\n", - " U_out[18] = 3.421202001397*U[0]*U[17] - 0.00102150709975934*U[10]**3*U[18] - 0.180063263231421*U[11]*U[6] + 0.7875*U[12]*U[4] - 0.7875*U[13]*U[5] - 0.288101221170275*U[14]*U[8] - 1.4625*U[15]*U[1] + 1.4625*U[16]*U[2] - 0.0374776161126912*U[17] - 0.043546511627907*U[18] + 5.47392320223521*U[19]*U[3] + 0.00725*U[7]\n", - " U_out[19] = -2.75575081119393*U[0]*U[20] - 0.000888267043268988*U[10]**3*U[19] - 0.250522801017629*U[11]*U[9] + 1.00209120407052*U[14]*U[7] - 4.00836481628207*U[18]*U[3] - 0.0473230535894843*U[19] + 0.0325892314023402*U[20] + 0.00024415290907648*U[22] - 2.35620151203137e-6*U[29]**3*U[31] - 0.000269281366471626*U[31] + 0.00819565217391304*U[8]\n", - " U_out[20] = 2.75575081119393*U[0]*U[19] - 0.000888267043268988*U[10]**3*U[20] + 0.250522801017629*U[11]*U[8] - 1.00209120407052*U[14]*U[6] + 4.00836481628207*U[17]*U[3] - 0.0325892314023402*U[19] - 0.0473230535894843*U[20] + 0.00819565217391304*U[9]\n", - " U_out[21] = 2.30554431681946e-7*U[12] + 2.5547312812995e-7*U[14] + 1.41879650265813e-7*U[17] - 2.30554431681946e-7*U[1] - 4.98594961275655e-7*U[21] - 0.000231547500016414*U[22]*U[25] - 0.000393263214313592*U[22]*U[27] - 0.00077917571434095*U[23]*U[26] - 0.00102174928578672*U[23]*U[28] - 0.00156202678582502*U[24]*U[27] - 2.49430228227486e-5*U[25] - 2.5547312812995e-7*U[3] - 1.41879650265813e-7*U[6]\n", - " U_out[22] = 1.27676499203106e-7*U[0] - 1.27676499203106e-7*U[11] + 4.43165421807241e-7*U[15] + 1.84356815471812e-7*U[19] - 0.000297563940911795*U[21]*U[25] + 0.00056941247952257*U[21]*U[27] - 1.45521268835226e-6*U[22] - 0.00185518259457354*U[23]*U[25] - 0.00363689261114416*U[24]*U[26] - 2.49312940023328e-5*U[26] - 4.43165421807241e-7*U[4] - 1.84356815471812e-7*U[8]\n", - " U_out[23] = -4.59275459509595e-7*U[14] - 0.00110122563125871*U[21]*U[26] + 0.0019601816236405*U[21]*U[28] + 0.000385428970940548*U[22]*U[25] - 3.04757790976247e-6*U[23] - 0.00547309138735578*U[24]*U[25] - 2.49117704610167e-5*U[27] + 4.59275459509595e-7*U[3]\n", - " U_out[24] = 5.09747214298137e-8*U[0] - 5.09747214298137e-8*U[11] - 0.0028417174170155*U[21]*U[27] - 0.000594010608460015*U[22]*U[26] + 0.00259237963074834*U[23]*U[25] - 5.2727039845599e-6*U[24] - 2.48844888171634e-5*U[28]\n", - " U_out[25] = -5.43087366736076e-7*U[13] + 0.000529111412123567*U[21]*U[22] + 2.49364240037345e-5*U[21] + 0.00146975392256546*U[22]*U[23] + 0.00288071768822831*U[23]*U[24] - 1.03680315467796e-6*U[25] + 5.43087366736076e-7*U[2]\n", - " U_out[26] = -1.04390781015316e-6*U[16] + 0.00188040062849988*U[21]*U[23] + 0.00423090141412473*U[22]*U[24] + 2.49247013868922e-5*U[22] - 1.99291491029239e-6*U[26] + 1.04390781015316e-6*U[5]\n", - " U_out[27] = -0.000176149545706673*U[21]*U[22] + 0.00440373864266683*U[21]*U[24] + 2.49051881654425e-5*U[23] - 3.58443843007766e-6*U[27]\n", - " U_out[28] = -0.000938435679208949*U[21]*U[23] + 2.4877920928742e-5*U[24] - 5.80838943890229e-6*U[28]\n", - " U_out[29] = 9.12059910499408e-6*U[10]**4 + 4.64878611115251e-5*U[10]**3*U[12] + 1.67415119447806e-5*U[10]**3*U[14] + 9.29757222230502e-6*U[10]**3*U[17] + 0.000521179402000664*U[10] + 0.000664115245473612*U[12] + 0.000239165516521407*U[14] + 0.000132823049094722*U[17] + 1.50119582264922*U[21]*U[35] - 0.750597911324609*U[21]*U[37] - 1.50119582263017*U[22]*U[34] + 1.12589686698056*U[22]*U[36] - 4.50358746792226*U[23]*U[35] + 2.25179373396113*U[23]*U[37] - 2.62709268963613*U[24]*U[34] - 5.62948433490282*U[24]*U[36] + 1.50119582263017*U[25]*U[31] + 2.62709268963613*U[25]*U[33] - 1.50119582264922*U[26]*U[30] + 4.50358746792226*U[26]*U[32] - 1.12589686698056*U[27]*U[31] + 5.62948433490282*U[27]*U[33] + 0.750597911324609*U[28]*U[30] - 2.25179373396113*U[28]*U[32] - 8.14339205803043e-7*U[29]**4 - 1.05879118406788e-22*U[29]**3*U[30] + 4.2351647362715e-22*U[29]**3*U[32] - 0.000260589700996678*U[29] + 5.70773716830428e-5\n", - " U_out[30] = 3.3881317890172e-21*U[10]**4 - 5.31652508387687e-5*U[10]**3*U[12] - 2.75103051082387e-5*U[10]**3*U[14] - 1.06330501677537e-5*U[10]**3*U[17] - 1.06685493772574e-16*U[10] - 0.000759506950146691*U[12] - 0.000393006100803128*U[14] - 0.000151901390029338*U[17] - 1.21682350103801*U[21]*U[35] + 0.608411750519007*U[21]*U[37] + 2.34182350103837*U[22]*U[34] - 1.28761762577863*U[22]*U[36] + 5.15047050311451*U[23]*U[35] - 2.57523525155726*U[23]*U[37] + 2.12944112681652*U[24]*U[34] + 6.43808812889314*U[24]*U[36] - 2.34182350103837*U[25]*U[31] - 2.12944112681652*U[25]*U[33] + 1.21682350103801*U[26]*U[30] - 5.15047050311451*U[26]*U[32] + 1.28761762577863*U[27]*U[31] - 6.43808812889314*U[27]*U[33] - 0.608411750519007*U[28]*U[30] + 2.57523525155726*U[28]*U[32] + 1.32348898008484e-22*U[29]**4 - 3.25735682321217e-6*U[29]**3*U[30] - 8.470329472543e-22*U[29]**3*U[32] - 0.000260589700996678*U[30] - 1.16551733542192e-17\n", - " U_out[31] = 2.78802933171573e-5*U[10]**3*U[11] - 1.54836099362061e-5*U[10]**3*U[15] - 3.09672198724122e-6*U[10]**3*U[19] + 0.000398291670074584*U[11] - 0.000221195408173121*U[15] - 4.42390816346243e-5*U[19] - 1.125*U[21]*U[34] + 0.375*U[21]*U[36] + 1.875*U[23]*U[34] + 2.25*U[24]*U[35] + 1.125*U[25]*U[30] - 1.875*U[25]*U[32] - 2.25*U[26]*U[33] - 0.375*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[31] - 0.000260589700996678*U[31] + 4.36192251724445e-5\n", - " U_out[32] = -8.470329472543e-22*U[10]**4 - 1.25605469675209e-5*U[10]**3*U[12] + 2.05688778355549e-5*U[10]**3*U[14] - 2.51210939350418e-6*U[10]**3*U[17] - 5.42101086242752e-20*U[10] - 0.000179437180657833*U[12] + 0.000293842414478535*U[14] - 3.58874361315667e-5*U[17] - 1.90560783367934*U[21]*U[35] + 0.952803916839669*U[21]*U[37] - 1.46939216632066*U[22]*U[34] - 0.304205875259503*U[22]*U[36] + 1.21682350103801*U[23]*U[35] - 0.608411750519007*U[23]*U[37] + 3.33481370893884*U[24]*U[34] + 1.52102937629752*U[24]*U[36] + 1.46939216632066*U[25]*U[31] - 3.33481370893884*U[25]*U[33] + 1.90560783367934*U[26]*U[30] - 1.21682350103801*U[26]*U[32] + 0.304205875259503*U[27]*U[31] - 1.52102937629752*U[27]*U[33] - 0.952803916839669*U[28]*U[30] + 0.608411750519007*U[28]*U[32] + 1.05879118406788e-22*U[29]**4 - 4.2351647362715e-22*U[29]**3*U[30] - 3.25735682321217e-6*U[29]**3*U[32] - 0.000260589700996678*U[32]\n", - " U_out[33] = 1.11521173268629e-5*U[10]**3*U[11] + 0.000159316668029834*U[11] - 1.875*U[21]*U[36] - 2.25*U[22]*U[35] - 2.625*U[23]*U[34] + 2.625*U[25]*U[32] + 2.25*U[26]*U[31] + 1.875*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[33] - 0.000260589700996678*U[33] + 1.74476900689778e-5\n", - " U_out[34] = 3.64823964199763e-5*U[10]**3*U[13] + 0.000521179401993355*U[13] + 1.125*U[21]*U[31] - 1.125*U[22]*U[30] + 1.875*U[22]*U[32] - 1.875*U[23]*U[31] + 2.625*U[23]*U[33] - 2.625*U[24]*U[32] - 3.25735682321217e-6*U[29]**3*U[34] - 0.000260589700996678*U[34]\n", - " U_out[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", - " U_out[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", - " U_out[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", - " return U_out" + "\t#Tendency function of the qgs model\n", + "\tU_out = np.empty_like(U)\n", + "\tU_out[0] = -0.0145*U[0] + 0.0145*U[11] + 0.0505574149661188*U[22] + 0.0734121368001177*U[24]\n", + "\tU_out[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[11]*U[13] + 0.0145*U[12] - 1.9945469157942*U[14]*U[16] - 2.59615384615385*U[15]*U[18] + 2.59615384615385*U[16]*U[17] - 0.0145*U[1] - 0.00295864958311857*U[21] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", + "\tU_out[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[11]*U[12] + 0.0145*U[13] + 1.9945469157942*U[14]*U[15] + 2.59615384615385*U[15]*U[17] + 2.59615384615385*U[16]*U[18] - 0.115315741885204*U[1] + 0.0145*U[25] - 0.0145*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", + "\tU_out[3] = 2.16075915877705*U[12]*U[16] - 2.16075915877705*U[13]*U[15] + 0.0145*U[14] + 4.32151831755411*U[17]*U[20] - 4.32151831755411*U[18]*U[19] + 2.16075915877705*U[1]*U[5] - 0.00216427290094687*U[21] + 0.0238416302768307*U[23] - 2.16075915877705*U[2]*U[4] - 0.0145*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", + "\tU_out[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[11]*U[16] + 2.43*U[12]*U[18] + 0.345721465404329*U[13]*U[14] - 2.43*U[13]*U[17] + 0.0145*U[15] + 2.43*U[1]*U[7] - 0.00449241352700723*U[22] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.0145*U[4] + 0.0599641857803059*U[5]\n", + "\tU_out[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[11]*U[15] - 0.345721465404329*U[12]*U[14] - 2.43*U[12]*U[17] - 2.43*U[13]*U[18] + 0.0145*U[16] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.0145*U[26] - 2.43*U[2]*U[7] - 0.0599641857803059*U[4] - 0.0145*U[5]\n", + "\tU_out[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[11]*U[18] + 0.675*U[12]*U[16] + 0.675*U[13]*U[15] - 5.18582198106493*U[14]*U[20] + 0.0145*U[17] + 0.675*U[1]*U[5] - 0.000192312222902707*U[21] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.0145*U[6] + 0.0749552322253824*U[7]\n", + "\tU_out[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[11]*U[17] - 0.675*U[12]*U[15] + 0.675*U[13]*U[16] + 5.18582198106493*U[14]*U[19] + 0.0145*U[18] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.0145*U[7]\n", + "\tU_out[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[11]*U[20] - 2.65939588772561*U[14]*U[18] + 0.0145*U[19] - 0.000431962839135311*U[22] - 2.65939588772561*U[3]*U[7] - 0.0145*U[8] + 0.0576578709426019*U[9]\n", + "\tU_out[9] = 2.65939588772561*U[0]*U[8] + 2.65939588772561*U[11]*U[19] + 2.65939588772561*U[14]*U[17] + 0.0145*U[20] + 2.65939588772561*U[3]*U[6] - 0.0576578709426019*U[8] - 0.0145*U[9]\n", + "\tU_out[10] = -0.000510753549879668*U[10]**4 - 0.014593023255814*U[10] - 0.00183644645351737*U[21] - 0.00374635076517544*U[23] + 1.59610484337396e-5*U[29]**4 + 5.17501542233338e-5*U[29]**3*U[30] + 1.72500514077779e-5*U[29]**3*U[32] + 0.00729651162790698*U[29] + 0.00591432955679692*U[30] + 0.00197144318559898*U[32] + 0.000531003677198668\n", + "\tU_out[11] = 0.00131818181818182*U[0] - 0.00185728563592607*U[10]**3*U[11] - 0.0198572938689218*U[11] - 1.63693875664928*U[12]*U[2] + 1.63693875664928*U[13]*U[1] - 1.30955100531943*U[15]*U[5] + 1.30955100531943*U[16]*U[4] - 3.27387751329857*U[17]*U[7] + 3.27387751329857*U[18]*U[6] - 2.61910201063885*U[19]*U[9] + 2.61910201063885*U[20]*U[8] - 0.00459612863328353*U[22] - 0.00667383061819252*U[24] + 4.43550120954776e-5*U[29]**3*U[31] + 1.7742004838191e-5*U[29]**3*U[33] + 0.00506916671004016*U[31] + 0.00202766668401607*U[33] + 0.000482730615635153\n", + "\tU_out[12] = -1.6647358298754*U[0]*U[13] - 0.0015418975090707*U[10]**3*U[12] + 1.05320021890077*U[11]*U[2] - 0.0287966213251426*U[12] + 0.0282849932925971*U[13] + 1.68512035024123*U[14]*U[5] + 1.06132075471698*U[15]*U[7] - 2.66357732780065*U[16]*U[3] - 1.06132075471698*U[16]*U[6] + 2.33490566037736*U[17]*U[5] - 2.33490566037736*U[18]*U[4] + 0.00355660377358491*U[1] + 0.000725706501519649*U[21] - 2.04500508591402e-5*U[29]**3*U[30] - 0.00233715902975374*U[30]\n", + "\tU_out[13] = 1.6647358298754*U[0]*U[12] - 0.0015418975090707*U[10]**3*U[13] - 1.05320021890077*U[11]*U[1] - 0.0282849932925971*U[12] - 0.0287966213251426*U[13] - 1.68512035024123*U[14]*U[4] + 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] - 1.06132075471698*U[16]*U[7] + 2.33490566037736*U[17]*U[4] + 2.33490566037736*U[18]*U[5] - 0.00355660377358491*U[25] + 4.81842971584593e-5*U[29]**3*U[34] + 0.00355660377358491*U[2] + 0.00550680122860904*U[34]\n", + "\tU_out[14] = -0.00145929585679905*U[10]**3*U[14] - 1.44050610585137*U[12]*U[5] + 1.44050610585137*U[13]*U[4] - 0.0311378737541528*U[14] - 2.67522562515254*U[15]*U[2] + 2.67522562515254*U[16]*U[1] - 2.88101221170274*U[17]*U[9] + 2.88101221170274*U[18]*U[8] - 5.35045125030509*U[19]*U[7] + 5.35045125030509*U[20]*U[6] + 0.000618363685984819*U[21] - 0.00681189436480877*U[23] - 1.74251833232233e-5*U[29]**3*U[30] + 3.1365329981802e-5*U[29]**3*U[32] - 0.00199145835037292*U[30] + 0.00358462503067126*U[32] + 0.00414285714285714*U[3]\n", + "\tU_out[15] = -1.35185957626052*U[0]*U[16] - 0.00125723950739611*U[10]**3*U[15] + 0.421071015556554*U[11]*U[5] - 0.45*U[12]*U[7] - 1.63996079743079*U[13]*U[3] + 0.45*U[13]*U[6] + 1.90590038620335*U[14]*U[2] - 0.0368649373881932*U[15] + 0.0230631483770407*U[16] - 2.31923076923077*U[17]*U[2] + 2.31923076923077*U[18]*U[1] + 0.00172785135654124*U[22] - 1.66746568543758e-5*U[29]**3*U[31] - 0.00190568351656843*U[31] + 0.00557692307692308*U[4]\n", + "\tU_out[16] = 1.35185957626052*U[0]*U[15] - 0.00125723950739611*U[10]**3*U[16] - 0.421071015556554*U[11]*U[4] + 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 0.45*U[13]*U[7] - 1.90590038620335*U[14]*U[1] - 0.0230631483770407*U[15] - 0.0368649373881932*U[16] - 2.31923076923077*U[17]*U[1] - 2.31923076923077*U[18]*U[2] - 0.00557692307692308*U[26] + 3.92887346061283e-5*U[29]**3*U[35] + 0.00449016100178891*U[35] + 0.00557692307692308*U[5]\n", + "\tU_out[17] = -3.421202001397*U[0]*U[18] - 0.00102150709975934*U[10]**3*U[17] + 0.180063263231421*U[11]*U[7] - 0.7875*U[12]*U[5] - 0.7875*U[13]*U[4] + 0.288101221170274*U[14]*U[9] + 1.4625*U[15]*U[2] + 1.4625*U[16]*U[1] - 0.043546511627907*U[17] + 0.0374776161126912*U[18] - 5.47392320223521*U[20]*U[3] + 9.61561114513534e-5*U[21] - 2.70963173883607e-6*U[29]**3*U[30] - 0.00030967357144237*U[30] + 0.00725*U[6]\n", + "\tU_out[18] = 3.421202001397*U[0]*U[17] - 0.00102150709975934*U[10]**3*U[18] - 0.180063263231421*U[11]*U[6] + 0.7875*U[12]*U[4] - 0.7875*U[13]*U[5] - 0.288101221170274*U[14]*U[8] - 1.4625*U[15]*U[1] + 1.4625*U[16]*U[2] - 0.0374776161126912*U[17] - 0.043546511627907*U[18] + 5.47392320223521*U[19]*U[3] + 0.00725*U[7]\n", + "\tU_out[19] = -2.75575081119393*U[0]*U[20] - 0.000888267043268988*U[10]**3*U[19] - 0.25052280101763*U[11]*U[9] + 1.00209120407052*U[14]*U[7] - 4.00836481628207*U[18]*U[3] - 0.0473230535894843*U[19] + 0.0325892314023402*U[20] + 0.00024415290907648*U[22] - 2.35620151203137e-6*U[29]**3*U[31] - 0.000269281366471626*U[31] + 0.00819565217391304*U[8]\n", + "\tU_out[20] = 2.75575081119393*U[0]*U[19] - 0.000888267043268988*U[10]**3*U[20] + 0.25052280101763*U[11]*U[8] - 1.00209120407052*U[14]*U[6] + 4.00836481628207*U[17]*U[3] - 0.0325892314023402*U[19] - 0.0473230535894843*U[20] + 0.00819565217391304*U[9]\n", + "\tU_out[21] = 2.30554431681946e-7*U[12] + 2.5547312812995e-7*U[14] + 1.41879650265813e-7*U[17] - 2.30554431681946e-7*U[1] - 4.98594961275655e-7*U[21] - 0.000231547500016414*U[22]*U[25] - 0.000393263214313592*U[22]*U[27] - 0.000779175714340949*U[23]*U[26] - 0.00102174928578672*U[23]*U[28] - 0.00156202678582502*U[24]*U[27] - 2.49430228227486e-5*U[25] - 2.5547312812995e-7*U[3] - 1.41879650265813e-7*U[6]\n", + "\tU_out[22] = 1.27676499203106e-7*U[0] - 1.27676499203106e-7*U[11] + 4.43165421807241e-7*U[15] + 1.84356815471812e-7*U[19] - 0.000297563940911795*U[21]*U[25] + 0.00056941247952257*U[21]*U[27] - 1.45521268835226e-6*U[22] - 0.00185518259457354*U[23]*U[25] - 0.00363689261114416*U[24]*U[26] - 2.49312940023328e-5*U[26] - 4.43165421807241e-7*U[4] - 1.84356815471812e-7*U[8]\n", + "\tU_out[23] = -4.59275459509595e-7*U[14] - 0.00110122563125871*U[21]*U[26] + 0.0019601816236405*U[21]*U[28] + 0.000385428970940548*U[22]*U[25] - 3.04757790976247e-6*U[23] - 0.00547309138735578*U[24]*U[25] - 2.49117704610167e-5*U[27] + 4.59275459509595e-7*U[3]\n", + "\tU_out[24] = 5.09747214298137e-8*U[0] - 5.09747214298137e-8*U[11] - 0.0028417174170155*U[21]*U[27] - 0.000594010608460015*U[22]*U[26] + 0.00259237963074834*U[23]*U[25] - 5.2727039845599e-6*U[24] - 2.48844888171634e-5*U[28]\n", + "\tU_out[25] = -5.43087366736076e-7*U[13] + 0.000529111412123567*U[21]*U[22] + 2.49364240037345e-5*U[21] + 0.00146975392256546*U[22]*U[23] + 0.00288071768822831*U[23]*U[24] - 1.03680315467796e-6*U[25] + 5.43087366736076e-7*U[2]\n", + "\tU_out[26] = -1.04390781015316e-6*U[16] + 0.00188040062849988*U[21]*U[23] + 0.00423090141412473*U[22]*U[24] + 2.49247013868922e-5*U[22] - 1.99291491029239e-6*U[26] + 1.04390781015316e-6*U[5]\n", + "\tU_out[27] = -0.000176149545706673*U[21]*U[22] + 0.00440373864266683*U[21]*U[24] + 2.49051881654425e-5*U[23] - 3.58443843007766e-6*U[27]\n", + "\tU_out[28] = -0.000938435679208948*U[21]*U[23] + 2.4877920928742e-5*U[24] - 5.80838943890229e-6*U[28]\n", + "\tU_out[29] = 9.12059910499408e-6*U[10]**4 + 4.64878611115251e-5*U[10]**3*U[12] + 1.67415119447806e-5*U[10]**3*U[14] + 9.29757222230502e-6*U[10]**3*U[17] + 0.000521179402000664*U[10] + 0.000664115245473612*U[12] + 0.000239165516521407*U[14] + 0.000132823049094722*U[17] + 1.50119582264922*U[21]*U[35] - 0.750597911324609*U[21]*U[37] - 1.50119582263017*U[22]*U[34] + 1.12589686698056*U[22]*U[36] - 4.50358746792226*U[23]*U[35] + 2.25179373396113*U[23]*U[37] - 2.62709268963613*U[24]*U[34] - 5.62948433490282*U[24]*U[36] + 1.50119582263017*U[25]*U[31] + 2.62709268963613*U[25]*U[33] - 1.50119582264922*U[26]*U[30] + 4.50358746792226*U[26]*U[32] - 1.12589686698056*U[27]*U[31] + 5.62948433490282*U[27]*U[33] + 0.750597911324609*U[28]*U[30] - 2.25179373396113*U[28]*U[32] - 8.14339205803043e-7*U[29]**4 - 1.05879118406788e-22*U[29]**3*U[30] + 4.2351647362715e-22*U[29]**3*U[32] - 0.000260589700996678*U[29] + 5.70773716830428e-5\n", + "\tU_out[30] = 3.3881317890172e-21*U[10]**4 - 5.31652508387687e-5*U[10]**3*U[12] - 2.75103051082387e-5*U[10]**3*U[14] - 1.06330501677537e-5*U[10]**3*U[17] - 1.06685493772574e-16*U[10] - 0.000759506950146691*U[12] - 0.000393006100803128*U[14] - 0.000151901390029338*U[17] - 1.21682350103801*U[21]*U[35] + 0.608411750519007*U[21]*U[37] + 2.34182350103837*U[22]*U[34] - 1.28761762577863*U[22]*U[36] + 5.15047050311451*U[23]*U[35] - 2.57523525155726*U[23]*U[37] + 2.12944112681652*U[24]*U[34] + 6.43808812889314*U[24]*U[36] - 2.34182350103837*U[25]*U[31] - 2.12944112681652*U[25]*U[33] + 1.21682350103801*U[26]*U[30] - 5.15047050311451*U[26]*U[32] + 1.28761762577863*U[27]*U[31] - 6.43808812889314*U[27]*U[33] - 0.608411750519007*U[28]*U[30] + 2.57523525155726*U[28]*U[32] + 1.32348898008484e-22*U[29]**4 - 3.25735682321217e-6*U[29]**3*U[30] - 8.470329472543e-22*U[29]**3*U[32] - 0.000260589700996678*U[30] - 1.16551733542192e-17\n", + "\tU_out[31] = 2.78802933171573e-5*U[10]**3*U[11] - 1.54836099362061e-5*U[10]**3*U[15] - 3.09672198724122e-6*U[10]**3*U[19] + 0.000398291670074584*U[11] - 0.000221195408173121*U[15] - 4.42390816346243e-5*U[19] - 1.125*U[21]*U[34] + 0.375*U[21]*U[36] + 1.875*U[23]*U[34] + 2.25*U[24]*U[35] + 1.125*U[25]*U[30] - 1.875*U[25]*U[32] - 2.25*U[26]*U[33] - 0.375*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[31] - 0.000260589700996678*U[31] + 4.36192251724445e-5\n", + "\tU_out[32] = -8.470329472543e-22*U[10]**4 - 1.25605469675209e-5*U[10]**3*U[12] + 2.05688778355549e-5*U[10]**3*U[14] - 2.51210939350418e-6*U[10]**3*U[17] - 5.42101086242752e-20*U[10] - 0.000179437180657833*U[12] + 0.000293842414478535*U[14] - 3.58874361315667e-5*U[17] - 1.90560783367934*U[21]*U[35] + 0.952803916839669*U[21]*U[37] - 1.46939216632066*U[22]*U[34] - 0.304205875259503*U[22]*U[36] + 1.21682350103801*U[23]*U[35] - 0.608411750519007*U[23]*U[37] + 3.33481370893884*U[24]*U[34] + 1.52102937629752*U[24]*U[36] + 1.46939216632066*U[25]*U[31] - 3.33481370893884*U[25]*U[33] + 1.90560783367934*U[26]*U[30] - 1.21682350103801*U[26]*U[32] + 0.304205875259503*U[27]*U[31] - 1.52102937629752*U[27]*U[33] - 0.952803916839669*U[28]*U[30] + 0.608411750519007*U[28]*U[32] + 1.05879118406788e-22*U[29]**4 - 4.2351647362715e-22*U[29]**3*U[30] - 3.25735682321217e-6*U[29]**3*U[32] - 0.000260589700996678*U[32]\n", + "\tU_out[33] = 1.11521173268629e-5*U[10]**3*U[11] + 0.000159316668029834*U[11] - 1.875*U[21]*U[36] - 2.25*U[22]*U[35] - 2.625*U[23]*U[34] + 2.625*U[25]*U[32] + 2.25*U[26]*U[31] + 1.875*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[33] - 0.000260589700996678*U[33] + 1.74476900689778e-5\n", + "\tU_out[34] = 3.64823964199763e-5*U[10]**3*U[13] + 0.000521179401993355*U[13] + 1.125*U[21]*U[31] - 1.125*U[22]*U[30] + 1.875*U[22]*U[32] - 1.875*U[23]*U[31] + 2.625*U[23]*U[33] - 2.625*U[24]*U[32] - 3.25735682321217e-6*U[29]**3*U[34] - 0.000260589700996678*U[34]\n", + "\tU_out[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", + "\tU_out[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", + "\tU_out[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", + "\treturn U_out\n" ] }, { @@ -422,7 +413,9 @@ "cell_type": "code", "execution_count": null, "id": "060ee686", - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "integrator = RungeKuttaIntegrator()\n", @@ -433,7 +426,9 @@ "cell_type": "code", "execution_count": null, "id": "d8935949", - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "integrator_num = RungeKuttaIntegrator()\n", @@ -447,18 +442,16 @@ "metadata": {}, "outputs": [], "source": [ - "ic = np.array([\n", - " 4.64467907e-02, -2.25054437e-02, -3.51762104e-02, 4.65057475e-03,\n", - " -5.08842464e-03, 1.92789965e-02, 3.47849539e-03, 1.86038254e-02,\n", - " -2.99742925e-03, 8.85862363e-03, 1.53577181e+00, 4.29584574e-02,\n", - " -1.88143295e-02, -1.15114614e-02, 1.24843108e-03, -3.61467108e-03,\n", - " 2.81459020e-03, 5.59630451e-03, 3.56804517e-03, -1.52654934e-03,\n", - " 3.09046486e-03, 1.18185262e-06, 6.76756261e-04, 4.21981618e-06,\n", - " 1.06290914e-05, -1.64056994e-05, 3.40932989e-05, 1.90943692e-05,\n", - " 1.08189600e-05, 3.16661484e+00, -3.06749318e-04, 1.82036792e-01,\n", - " -1.33924039e-04, 9.77967977e-03, -5.58853689e-03, 2.33218135e-02,\n", - " 3.45971396e-05, -5.88894363e-05\n", - "])" + "ic = np.array([ 2.47290678e-02, 1.06856407e-05, 6.14020503e-06, 1.33706189e-04,\n", + " -2.74726085e-03, -1.01933296e-03, -2.86432834e-05, 2.28111414e-05,\n", + " 4.02242178e-04, -3.13199876e-04, 1.53917062e+00, 2.45251037e-02,\n", + " 4.50183572e-05, 5.83105679e-05, 1.15511398e-04, -2.60952597e-03,\n", + " -9.70417246e-04, 1.52876906e-06, 5.43161032e-06, -1.02860232e-04,\n", + " -7.41358265e-05, 5.98019639e-08, 6.33276870e-05, 2.96344268e-07,\n", + " 5.91166499e-07, -1.46173592e-06, -4.48474858e-04, -5.03271939e-06,\n", + " -9.64945641e-06, 3.17631680e+00, -6.74609231e-04, 3.79257330e-02,\n", + " 4.48058789e-04, -4.14799637e-02, 3.04296494e-04, -6.79015121e-03,\n", + " -1.17146260e-05, 1.47111610e-07])" ] }, { @@ -469,7 +462,7 @@ "outputs": [], "source": [ "%%time\n", - "integrator.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", + "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", "reference_time, reference_traj = integrator.get_trajectories()" ] }, @@ -481,7 +474,7 @@ "outputs": [], "source": [ "%%time\n", - "integrator_num.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", + "integrator_num.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" ] }, @@ -506,37 +499,25 @@ { "cell_type": "code", "execution_count": null, - "id": "3ce3d705", - "metadata": {}, - "outputs": [], - "source": [ - "reference_traj.shape" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "45fc0093", + "id": "5e3b5782", "metadata": {}, "outputs": [], "source": [ - "error = reference_traj - reference_traj_num" + "reference_traj[:, -1]" ] }, { "cell_type": "code", "execution_count": null, - "id": "5e3b5782", + "id": "69a02871", "metadata": {}, "outputs": [], - "source": [ - "error[:, 1]" - ] + "source": [] }, { "cell_type": "code", "execution_count": null, - "id": "54b31bdb", + "id": "8d423db0", "metadata": {}, "outputs": [], "source": [] diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index e25da7c..dfd1a20 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -857,7 +857,7 @@ def subs_tensor(self, tensor=None, dict_opp=True, variables=None, remain_variabl except: ten_out[key] = val - elif dict_opp and self.tensor_dic is not None: + elif dict_opp: ten_out = dict() for key in self.tensor_dic.keys(): val = self.tensor_dic[key].subs(symbol_to_number_map) From 547cbed5f8dfd4b8ce0dc6add75514b0aaa1ef24 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 10 Aug 2023 15:41:29 +0200 Subject: [PATCH 039/143] Cleaning up Linear notebook --- notebooks/Symbolic Output-Linear.ipynb | 144 +++++++++++++++++++------ qgs/functions/symbolic_output.py | 2 +- 2 files changed, 110 insertions(+), 36 deletions(-) diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 1fc0e18..e60ba8d 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -1,5 +1,21 @@ { "cells": [ + { + "cell_type": "markdown", + "id": "037e82a6", + "metadata": {}, + "source": [ + "# Linear symbolic MAOOAM example " + ] + }, + { + "cell_type": "markdown", + "id": "57e1a1bb", + "metadata": {}, + "source": [ + "Testing platform for the symbolic equation version of the qgs model" + ] + }, { "cell_type": "code", "execution_count": null, @@ -92,7 +108,7 @@ "source": [ "%%time\n", "# Takes ~5 mins\n", - "# ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", + "ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True) # <- Can be turned off once saved\n", "ocn_ip = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, @@ -107,7 +123,7 @@ "source": [ "%%time\n", "# Takes ~5 mins\n", - "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", + "atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True) # <- Can be turned off once saved\n", "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, @@ -118,8 +134,8 @@ "metadata": {}, "outputs": [], "source": [ - "# atm_ip_stored.save_to_file('temp_atm_sym_lin.ip')\n", - "# ocn_ip_stored.save_to_file('temp_ocn_sym_lin.ip')" + "atm_ip_stored.save_to_file('temp_atm_sym_lin.ip') # <- Can be turned off once saved\n", + "ocn_ip_stored.save_to_file('temp_ocn_sym_lin.ip')" ] }, { @@ -131,7 +147,7 @@ }, "outputs": [], "source": [ - "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)\n", + "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True) \n", "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" ] }, @@ -161,7 +177,7 @@ "id": "d43d341b", "metadata": {}, "source": [ - "## Testing the symbolic_qgtensor" + "## Outputting model equations" ] }, { @@ -174,6 +190,14 @@ "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, print_equations, equation_as_function" ] }, + { + "cell_type": "markdown", + "id": "8ddc3286", + "metadata": {}, + "source": [ + "Calculating the functions and tensor" + ] + }, { "cell_type": "code", "execution_count": null, @@ -188,13 +212,12 @@ ] }, { - "cell_type": "code", - "execution_count": null, - "id": "c758a21b", + "cell_type": "markdown", + "id": "0c185e14", "metadata": {}, - "outputs": [], "source": [ - "fe = print_equations(funcs, model_parameters, return_equations=True)" + "Convert the dictionary of functions to a lambdified function (not working with numba)\n", + "The input `remain_variables` specifies which variables not to substitute with values." ] }, { @@ -211,56 +234,51 @@ { "cell_type": "code", "execution_count": null, - "id": "c385c4be", - "metadata": { - "scrolled": true - }, + "id": "3ec572eb", + "metadata": {}, "outputs": [], "source": [ - "for i in fx:\n", - " print(i)" + "from numba import njit" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "005608b6", + "cell_type": "markdown", + "id": "0340486e", "metadata": {}, - "outputs": [], "source": [ - "np.array(list(fe.values()))" + "Produce the tendencies as a string and output this string defining the below function" ] }, { "cell_type": "code", "execution_count": null, - "id": "77b88c4f", + "id": "38012b9c", "metadata": {}, "outputs": [], "source": [ - "x =np.random.rand(36)" + "%%time\n", + "function_text = equation_as_function(funcs, model_parameters, remain_variables={'eps', 'n'}, language='python', string_output=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "3ec572eb", - "metadata": {}, + "id": "c385c4be", + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "from numba import njit" + "for i in function_text:\n", + " print(i)" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "91fda04c", + "cell_type": "markdown", + "id": "2bd791a9", "metadata": {}, - "outputs": [], "source": [ - "@njit\n", - "def f(U):\n", - " return fx(U, n=1.5, epsilon=0.76)" + "Below function has the **kwargs input removed as I cannot get this to work with numba" ] }, { @@ -315,6 +333,14 @@ "\treturn U_out" ] }, + { + "cell_type": "markdown", + "id": "0579478b", + "metadata": {}, + "source": [ + "## Comparing with numerical results" + ] + }, { "cell_type": "code", "execution_count": null, @@ -435,10 +461,58 @@ "plt.show()" ] }, + { + "cell_type": "markdown", + "id": "1417d44f", + "metadata": {}, + "source": [ + "### Testing lambda function" + ] + }, + { + "cell_type": "markdown", + "id": "ea6f7466", + "metadata": {}, + "source": [ + "I dont know enough about numba to work out how to make the lambified version work here" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e49d480b", + "metadata": {}, + "outputs": [], + "source": [ + "fx(0, ic, epsilon=0.76, n=1.3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91fda04c", + "metadata": {}, + "outputs": [], + "source": [ + "@njit\n", + "def f_test(t, U):\n", + " return fx(t, U, n=1.5, epsilon=0.76)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1eeb348", + "metadata": {}, + "outputs": [], + "source": [ + "f_test(0, ic)" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "9f8ddaac", + "id": "76ecb077", "metadata": {}, "outputs": [], "source": [] diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 0f6d6aa..6616583 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -273,7 +273,7 @@ def equation_as_function(equations, params, string_output=False, language='pytho # Return a lamdafied function vec = [sy.Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] array_eqs = np.array(list(eq_list.values())) - inputs = [vec] + inputs = ['t', vec] for v in free_vars: inputs.append(v) f_output = sy.lambdify(inputs, array_eqs) From 014402c69f47e9f9cfe9f9dd83e0e3e814ff3346 Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 16 Aug 2023 17:49:20 +0200 Subject: [PATCH 040/143] Bug fixes for ground version --- qgs/functions/symbolic_output.py | 167 ++++++++++++++++++------------- qgs/params/params.py | 6 +- qgs/tensors/symbolic_qgtensor.py | 14 ++- 3 files changed, 110 insertions(+), 77 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 6616583..01d6cd1 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -14,6 +14,7 @@ fortran_lang_translation = { '**': '^' + #TODO: Is there a reason that sqrt is replaced with sq2 in auto? For computational speedup? } julia_lang_translation = { @@ -126,6 +127,9 @@ def translate_equations(equations, language='python'): if language == 'julia': translator = julia_lang_translation + if language == 'fortran': + translator = fortran_lang_translation + # translate mathematical operations if isinstance(equations, dict): str_eq = dict() @@ -143,9 +147,9 @@ def translate_equations(equations, language='python'): return str_eq -def print_equations(equations, params, save_loc=None, language='python', variables=True, remain_variables=dict(), return_equations=False): +def format_equations(equations, params, save_loc=None, language='python', variables=True, remain_variables=dict(), print_equations=False): ''' - Function prints the equations as strings, in the programming language specified, and saves the equations to the specified location. + Function formats the equations, in the programming language specified, and saves the equations to the specified location. The variables in the equation are substituted if the model variable is input. Parameters @@ -171,59 +175,31 @@ def print_equations(equations, params, save_loc=None, language='python', variabl ''' - if equations is not None: - equation_dict = dict() - if variables is not None: - # make a dictionary of variables to substitute from parameters - sub_vals = dict() - if variables == True: - for key in params.symbol_to_value.keys(): - if key not in remain_variables: - sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] - else: - if isinstance(variables, (set, list, dict)): - for s in variables: - if isinstance(s, str): - temp_sym, val = params.symbol_to_value[s] - elif isinstance(s, sy.Symbol): - temp_sym = s - val = params.symbol_to_value[temp_sym] - else: - raise ValueError("Incorrect type for substitution, needs to be string or sympy.Symbol, not: " + str(type(s))) - sub_vals[temp_sym] = val - else: - raise ValueError("Incorrect type for substitution, needs to be list, set, or dictionary, not: " + str(type(variables))) - - - for k in equations.keys(): - eq = equations[k] - if sub_vals is not None: - eq = eq.subs(sub_vals) - eq = eq.evalf() - - if (language is not None) and not(return_equations): - eq = translate_equations(eq, language) - - if save_loc is None: - equation_dict[k] = eq - else: - equation_dict[k] = str(eq) - - if return_equations: - return equation_dict + # Substitute variables + equation_dict = dict() + sub_vals = None + if variables is not None: + # make a dictionary of variables to substitute from parameters + sub_vals = dict() + if variables == True: + for key in params.symbol_to_value.keys(): + if key not in remain_variables: + sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] else: - if save_loc is None: - for eq in equation_dict.values(): - if save_loc is None: - print(eq) - else: - with open(save_loc, 'w') as f: - for eq in equation_dict.values(): - f.write("%s\n" % eq) - print("Equations written") - -def equation_as_function(equations, params, string_output=False, language='python', variables=True, remain_variables=dict()): + if isinstance(variables, (set, list, dict)): + for s in variables: + if isinstance(s, str): + temp_sym, val = params.symbol_to_value[s] + elif isinstance(s, sy.Symbol): + temp_sym = s + val = params.symbol_to_value[temp_sym] + else: + raise ValueError("Incorrect type for substitution, needs to be string or sympy.Symbol, not: " + str(type(s))) + sub_vals[temp_sym] = val + else: + raise ValueError("Incorrect type for substitution, needs to be list, set, or dictionary, not: " + str(type(variables))) + # Substitute variable symbols vector_subs = dict() if language == 'python': for i in range(1, params.ndim+1): @@ -240,58 +216,105 @@ def equation_as_function(equations, params, string_output=False, language='pytho if language == 'mathematica': for i in range(1, params.ndim+1): vector_subs['U_'+str(i)] = sy.Symbol('U('+str(i)+')') - - subed_eq = dict() + free_vars = set() for k in equations.keys(): - subed_eq[k] = equations[k].subs(vector_subs) + eq = equations[k].subs(vector_subs) - eq_list = print_equations(subed_eq, params, language=language, variables=variables, remain_variables=remain_variables, return_equations=True) - - # Calculate the free variables - free_vars = set() - for func in eq_list.values(): - for vars in func.free_symbols: + #substitute syntax + if sub_vals is not None: + eq = eq.subs(sub_vals) + eq = eq.evalf() + else: + eq = eq.simplify() + for vars in eq.free_symbols: if vars not in vector_subs.values(): free_vars.add(vars) + if (language is not None) and print_equations: + eq = translate_equations(eq, language) + + equation_dict[k] = eq + + if print_equations: + if save_loc is None: + for eq in equation_dict.values(): + if save_loc is None: + print(eq) + else: + with open(save_loc, 'w') as f: + for eq in equation_dict.values(): + f.write("%s\n" % eq) + print("Equations written") + else: + return equation_dict, free_vars + +def equation_as_function(equations, params, string_output=False, language='python', variables=True, remain_variables=dict()): + + eq_dict, free_vars = format_equations(equations, params, language=language, variables=variables, remain_variables=remain_variables) + if language == 'python': if string_output: f_output = list() f_output.append('def f(t, U, **kwargs):') f_output.append('\t#Tendency function of the qgs model') - f_output.append('\tU_out = np.empty_like(U)') + f_output.append('\tF = np.empty_like(U)') for v in free_vars: f_output.append('\t' + str(v) + " = kwargs['" + str(v) + "']") - for n, eq in enumerate(eq_list.values()): - f_output.append('\tU_out['+str(n)+'] = ' + str(eq)) + for n, eq in enumerate(eq_dict.values()): + f_output.append('\tF['+str(n)+'] = ' + str(eq)) - f_output.append('\treturn U_out') + f_output.append('\treturn F') else: # Return a lamdafied function vec = [sy.Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] - array_eqs = np.array(list(eq_list.values())) + array_eqs = np.array(list(eq_dict.values())) inputs = ['t', vec] + for v in free_vars: inputs.append(v) + f_output = sy.lambdify(inputs, array_eqs) if language == 'julia': - eq_list = translate_equations(eq_list, language='julia') + eq_dict = translate_equations(eq_dict, language='julia') f_output = list() f_output.append('function f(t, U, kwargs...)') f_output.append('\t#Tendency function of the qgs model') f_output.append('\tU_out = similar(U)') - for n, eq in enumerate(eq_list.values()): - f_output.append('\tU_out['+str(n+1)+'] = ' + str(eq)) + for n, eq in enumerate(eq_dict.values()): + f_output.append('\tF['+str(n+1)+'] = ' + str(eq)) - f_output.append('\treturn U_out') + f_output.append('\treturn F') f_output.append('end') #//TODO: Add statement for Fortran #//TODO: Add statement for mathematica - return f_output \ No newline at end of file + return f_output + +def equation_to_auto(equations, params, remain_variables=dict()): + # User passes the equations, with the variables to leave as variables. + # The existing model parameters are used to populate the auto file + # The variables given as `remain_variables` remain in the equations. + # There is a limit of 1-10 remian variables + + if (len(remain_variables) < 1) or (len(remain_variables) > 10): + ValueError("Too many variables for auto file") + + str_equations, free_variables = format_equations(equations=equations, params=params, language='fortran', variables=True) + + natm, nog = params.nmod + dim = params.ndim + offset = 1 if params.dynamic_T else 0 + + # make list of free variables + var_list = list() + for i, fv in enumerate(free_variables): + temp_str = "PAR(" + str(i) + ") = " + str(fv) + var_list.append(temp_str) + + \ No newline at end of file diff --git a/qgs/params/params.py b/qgs/params/params.py index 4df2c4e..09763d2 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -2108,7 +2108,8 @@ def _set_symbolic_parameters(self): # Ground Parameters if self.ground_params is not None: self.symbol_to_value['hk_val'] = (self.symbolic_params['hk_val'], self.ground_params.hk) - self.symbol_to_value['hk'] = (self.symbolic_params['hk'], self.ground_params.hk) + #TODO The index here should not be hard coded + self.symbol_to_value['hk'] = (self.symbolic_params['hk_val'], self.ground_params.hk[1]) #//TODO: Fix the bogde on the index of the insolation # Ground Temperature Parameters @@ -2117,7 +2118,10 @@ def _set_symbolic_parameters(self): # Ground/ocean Parameters if self.gotemperature_params is not None: + #TODO The index here should not be hard coded self.symbol_to_value['go_C_val'] = (self.symbolic_params['go_C_val'], self.gotemperature_params.C[0]) + + #TODO The index here should not be hard coded self.symbol_to_value['go_C'] = (self.symbolic_params['go_C_val'], self.gotemperature_params.C[0]) self.symbol_to_value['go_T0'] = (self.symbolic_params['go_T0'], self.gotemperature_params.T0) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index dfd1a20..a4ff242 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -334,7 +334,9 @@ def _compute_tensor_dicts(self): oro = _symbolic_tensordot(a_inv_mult_gh, symbolic_params['hk'], axes=1) a_inv_mult_b = _symbolic_tensordot(a_inv, aips._b[offset:, offset:, offset:], axes=1) - a_inv_mult_d = a_inv @ aips._d[offset:, offset:] + + if ocean: + a_inv_mult_d = a_inv @ aips._d[offset:, offset:] for i in range(nvar[0]): @@ -349,7 +351,7 @@ def _compute_tensor_dicts(self): # convert if symbolic_params['hk'] is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), oro[i, j][0] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -oro[i, j][0] / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j][0] / 2) for k in range(nvar[0]): @@ -360,6 +362,7 @@ def _compute_tensor_dicts(self): if ocean: for j in range(nvar[2]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * symbolic_params['kd'] / 2) + # theta_a part a_theta_mult_u = _symbolic_tensordot(a_theta, aips._u, axes=1) if self.Cpa is not None: @@ -382,10 +385,13 @@ def _compute_tensor_dicts(self): a_theta_mult_b = _symbolic_tensordot(a_theta, aips._b[:, offset:, offset:], axes=1) - if ocean or ground_temp: + if ocean: a_theta_mult_d = _symbolic_tensordot(a_theta, aips._d[:, offset:], axes=1) a_theta_mult_s = _symbolic_tensordot(a_theta, aips._s, axes=1) + if ground_temp: + a_theta_mult_s = _symbolic_tensordot(a_theta, aips._s, axes=1) + for i in range(nvar[1]): if self.Cpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i][0]) @@ -494,7 +500,7 @@ def _compute_tensor_dicts(self): U_inv_mult_W = _symbolic_tensordot(U_inv, bips._W, axes=1) U_inv_mult_W_Cpgo = _symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) for i in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i]) # not perfect + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i][0]) # not perfect for j in range(nvar[1]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * symbolic_params['sc'] * self.Lpgo) From e7f03cdaa22e3e44d99a09d3cc7b8d7d42c831a9 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 17 Aug 2023 17:39:32 +0200 Subject: [PATCH 041/143] Addition of all variables --- qgs/functions/symbolic_output.py | 1 + qgs/params/params.py | 6 +++++- qgs/tensors/symbolic_qgtensor.py | 20 ++++++++++---------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 01d6cd1..070c2f7 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -316,5 +316,6 @@ def equation_to_auto(equations, params, remain_variables=dict()): for i, fv in enumerate(free_variables): temp_str = "PAR(" + str(i) + ") = " + str(fv) var_list.append(temp_str) + \ No newline at end of file diff --git a/qgs/params/params.py b/qgs/params/params.py index 09763d2..687ca8b 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -856,6 +856,8 @@ class QgParams(Params): 'fo': sy.Symbol('f0'), 'beta': sy.Symbol('beta'), 'n': sy.Symbol('n', positive=True), + 'rr': sy.Symbol('R'), + 'sb': sy.Symbol('sigma_b'), # Atmosphere Parameters 'kd': sy.Symbol('k_d'), @@ -2083,7 +2085,9 @@ def _set_symbolic_parameters(self): self.symbol_to_value['fo'] = (self.symbolic_params['fo'], self.scale_params.f0) self.symbol_to_value['beta'] = (self.symbolic_params['beta'], self.scale_params.beta) self.symbol_to_value['n'] = (self.symbolic_params['n'], self.scale_params.n) - + self.symbol_to_value['rr'] = (self.symbolic_params['rr'], self.rr) + self.symbol_to_value['sb'] = (self.symbolic_params['sb'], self.sb) + # Atmosphere Parameters if self.atmospheric_params is not None: self.symbol_to_value['kd'] = (self.symbolic_params['kd'], self.atmospheric_params.kd) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index a4ff242..7880ad1 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -171,7 +171,7 @@ def G(self): @property def Cpgo(self): - return self.sym_params['go_C'] / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) * self.params.rr / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) + return self.sym_params['go_C'] / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) * self.sym_params['rr'] / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) @property def Lpgo(self): @@ -179,7 +179,7 @@ def Lpgo(self): @property def Cpa(self): - return self.sym_params['atm_C'] / (self.sym_params['atm_gamma'] * self.sym_params['fo']) * self.params.rr / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) / 2 + return self.sym_params['atm_C'] / (self.sym_params['atm_gamma'] * self.sym_params['fo']) * self.sym_params['rr'] / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) / 2 @property def Lpa(self): @@ -192,44 +192,44 @@ def sbpgo(self): if self.params.gotemperature_params.T0 is None: None else: - return 4 * self.params.sb * self.sym_params['go_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + return 4 * self.sym_params['sb'] * self.sym_params['go_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) @property def sbpa(self): if self.params.atemperature_params.T0 is None: return None else: - return 8 * self.sym_params['eps'] * self.params.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + return 8 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['atm_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) @property def LSBpgo(self): if self.params.gotemperature_params.T0 is None: None else: - return 2 * self.sym_params['eps'] * self.params.sb * self.sym_params['go_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + return 2 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['go_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) @property def LSBpa(self): if self.params.atemperature_params.T0 is None: return None else: - return 8 * self.sym_params['eps'] * self.params.sb * self.sym_params['atm_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + return 8 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['atm_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) @property def T4sbpgo(self): - return self.params.sb * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['gnd_gamma'] * self.params.rr ** 3) + return self.sym_params['sb'] * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['gnd_gamma'] * self.sym_params['rr'] ** 3) @property def T4sbpa(self): - return 16 * self.sym_params['eps'] * self.params.sb * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['gnd_gamma'] * self.params.rr ** 3) + return 16 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['gnd_gamma'] * self.sym_params['rr'] ** 3) @property def T4LSBpgo(self): - return 0.5 * self.sym_params['eps'] * self.params.sb * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['atm_gamma'] * self.params.rr ** 3) + return 0.5 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['atm_gamma'] * self.sym_params['rr'] ** 3) @property def T4LSBpa(self): - return 16 * self.sym_params['eps'] * self.params.sb * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['atm_gamma'] * self.params.rr ** 3) + return 16 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['atm_gamma'] * self.sym_params['rr'] ** 3) #//TODO: Do i need the scaling parameters? From 613f60bae34813b76e861af99795bd86422bb70c Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 18 Aug 2023 13:49:08 +0200 Subject: [PATCH 042/143] Inner products can be calculated numerically and output symbolically --- qgs/functions/symbolic_output.py | 72 ++--- qgs/inner_products/symbolic.py | 470 ++++++++++++++++++++----------- qgs/tensors/symbolic_qgtensor.py | 71 +---- 3 files changed, 342 insertions(+), 271 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 070c2f7..3196af7 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -1,7 +1,8 @@ import numpy as np import sympy as sy -from qgs.functions.symbolic_mul import _symbolic_tensordot, symbolic_sparse_mult2, symbolic_sparse_mult3, symbolic_sparse_mult4, symbolic_sparse_mult5 +import warnings +from qgs.functions.symbolic_mul import symbolic_sparse_mult2, symbolic_sparse_mult3, symbolic_sparse_mult4, symbolic_sparse_mult5 from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, GroundSymbolicInnerProducts @@ -26,20 +27,26 @@ '**': '^' } -def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, simplify=False, return_inner_products=False, return_jacobian=False, return_symbolic_dict=False, return_symbolic_qgtensor=False, numerically_test_tensor=True): +def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables={}, language='python', return_inner_products=False, return_jacobian=False, return_symbolic_eqs=False, return_symbolic_qgtensor=False): """ Function to output the raw symbolic functions of the qgs model. """ + if 'n' in continuation_variables: + make_ip_subs = False + warnings.warn("Calculating innerproducts symbolically, as the variable 'n' has been specified as a variable, this takes several minutes.") + else: + make_ip_subs = True + if params.atmospheric_basis is not None: if atm_ip is None: - aip = AtmosphericSymbolicInnerProducts(params, return_symbolic=True) + aip = AtmosphericSymbolicInnerProducts(params, return_symbolic=True, make_substitution=make_ip_subs) else: aip = atm_ip else: aip = None if params.oceanic_basis is not None: if ocn_ip is None: - oip = OceanicSymbolicInnerProducts(params, return_symbolic=True) + oip = OceanicSymbolicInnerProducts(params, return_symbolic=True, make_substitution=make_ip_subs) else: oip = ocn_ip else: @@ -47,7 +54,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, sim if params.ground_basis is not None: if gnd_ip is None: - gip = GroundSymbolicInnerProducts(params, return_symbolic=True) + gip = GroundSymbolicInnerProducts(params, return_symbolic=True, make_substitution=make_ip_subs) else: gip = gnd_ip else: @@ -61,11 +68,11 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, sim aip.connect_to_ground(gip) if params.T4: - agotensor = SymbolicTensorT4(params, aip, oip, gip, numerically_test_tensor) + agotensor = SymbolicTensorT4(params, aip, oip, gip) elif params.dynamic_T: - agotensor = SymbolicTensorDynamicT(params, aip, oip, gip, numerically_test_tensor) + agotensor = SymbolicTensorDynamicT(params, aip, oip, gip) else: - agotensor = SymbolicTensorLinear(params, aip, oip, gip, numerically_test_tensor) + agotensor = SymbolicTensorLinear(params, aip, oip, gip) xx = list() xx.append(1) @@ -86,7 +93,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, sim eq_simplified = dict() Deq_simplified = dict() - if simplify: + if continuation_variables is None: # Simplifying at this step is slow # This only needs to be used if no substitutions are being made for i in range(1, params.ndim+1): @@ -95,21 +102,24 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, sim for j in range(1, params.ndim+1): if (i, j) in Deq: Deq_simplified[(i, j)] = Deq[(i, j)].simplify() + else: eq_simplified = eq if return_jacobian: Deq_simplified = Deq - + + funcs = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, remain_variables=continuation_variables) + ret = list() - ret.append(eq_simplified) + ret.append('\n'.join(funcs)) if return_jacobian: ret.append(Deq_simplified) if return_inner_products: ret.append((aip, oip, gip)) - if return_symbolic_dict: - ret.append(agotensor.tensor_dic) + if return_symbolic_eqs: + ret.append(eq_simplified) if return_symbolic_qgtensor: - ret.append(agotensor.tensor) + ret.append(agotensor.tensor_dic) return ret def translate_equations(equations, language='python'): @@ -147,7 +157,7 @@ def translate_equations(equations, language='python'): return str_eq -def format_equations(equations, params, save_loc=None, language='python', variables=True, remain_variables=dict(), print_equations=False): +def format_equations(equations, params, save_loc=None, language='python', remain_variables={}, print_equations=False): ''' Function formats the equations, in the programming language specified, and saves the equations to the specified location. The variables in the equation are substituted if the model variable is input. @@ -177,27 +187,21 @@ def format_equations(equations, params, save_loc=None, language='python', variab ''' # Substitute variables equation_dict = dict() - sub_vals = None - if variables is not None: + if isinstance(remain_variables, (set, list, dict)): # make a dictionary of variables to substitute from parameters sub_vals = dict() - if variables == True: - for key in params.symbol_to_value.keys(): + for key in params.symbol_to_value.keys(): + if len(remain_variables) == 0: + sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] + else: if key not in remain_variables: sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] - else: - if isinstance(variables, (set, list, dict)): - for s in variables: - if isinstance(s, str): - temp_sym, val = params.symbol_to_value[s] - elif isinstance(s, sy.Symbol): - temp_sym = s - val = params.symbol_to_value[temp_sym] - else: - raise ValueError("Incorrect type for substitution, needs to be string or sympy.Symbol, not: " + str(type(s))) - sub_vals[temp_sym] = val - else: - raise ValueError("Incorrect type for substitution, needs to be list, set, or dictionary, not: " + str(type(variables))) + + elif remain_variables is None: + sub_vals = None + + else: + raise ValueError("Incorrect type for substitution, needs to be set, list, or dict of strings, not: " + str(type(remain_variables))) # Substitute variable symbols vector_subs = dict() @@ -249,9 +253,9 @@ def format_equations(equations, params, save_loc=None, language='python', variab else: return equation_dict, free_vars -def equation_as_function(equations, params, string_output=False, language='python', variables=True, remain_variables=dict()): +def equation_as_function(equations, params, string_output=False, language='python', remain_variables={}): - eq_dict, free_vars = format_equations(equations, params, language=language, variables=variables, remain_variables=remain_variables) + eq_dict, free_vars = format_equations(equations, params, language=language, remain_variables=remain_variables) if language == 'python': if string_output: diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 5c4ed52..c5f1beb 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -103,7 +103,7 @@ class AtmosphericSymbolicInnerProducts(AtmosphericInnerProducts): """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False, make_substitution=True): AtmosphericInnerProducts.__init__(self) @@ -174,10 +174,15 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.return_symbolic = return_symbolic if return_symbolic: - self._p_compute = _symbolic_compute - self.subs = None #[('n', params.symbolic_params['n'])] + self.mk_subs = make_substitution + # _parallel_compute = _symbolic_compute + if self.mk_subs: + self.subs = [('n', self.n)] + else: + self.subs = None else: - self._p_compute = _parallel_compute + self.mk_subs = True + # _parallel_compute = _parallel_compute self.subs = [('n', self.n)] if inner_product_definition is None: @@ -240,23 +245,29 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + self.oceanic_basis.substitutions + else: + subs = self.subs noc = len(ocean_basis) - self._gh = None - self._d = sp.zeros((self.natm, noc), dtype=float, format='dok') - self._s = sp.zeros((self.natm, noc), dtype=float, format='dok') - if self._T4 or self._dynamic_T: - self._v = sp.zeros((self.natm, noc, noc, noc, noc), dtype=float, format='dok') + if self.return_symbolic: + self._gh = None + self._d = None + self._s = None + self._v = None + else: + self._gh = None + self._d = sp.zeros((self.natm, noc), dtype=float, format='dok') + self._s = sp.zeros((self.natm, noc), dtype=float, format='dok') + if self._T4 or self._dynamic_T: + self._v = sp.zeros((self.natm, noc, noc, noc, noc), dtype=float, format='dok') # d inner products args_list = [[(i, j), self.iip.ip_lap, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(noc)] - output = self._p_compute(pool, args_list, subs, self._d, timeout) + output = _parallel_compute(pool, args_list, subs, self._d, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._d = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, noc, output) else: @@ -266,7 +277,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): args_list = [[(i, j), self.iip.symbolic_inner_product, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(noc)] - output = self._p_compute(pool, args_list, subs, self._s, timeout) + output = _parallel_compute(pool, args_list, subs, self._s, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._s = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, noc, output) else: @@ -277,13 +288,13 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.natm) for j in range(noc) for k in range(j, noc) for ell in range(k, noc) for m in range(ell, noc)] - output = self._p_compute(pool, args_list, subs, self._v, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not(self.mk_subs)) elif self._dynamic_T: # v inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.natm) for m in range(noc)] - output = self._p_compute(pool, args_list, subs, self._v, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not(self.mk_subs)) if self._T4 or self._dynamic_T: @@ -326,26 +337,32 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + self.ground_basis.substitutions + else: + subs = self.subs ngr = len(ground_basis) - if orographic_basis == "atmospheric": + if self.return_symbolic: self._gh = None + self._d = None + self._s = None + self._v = None else: - self._gh = sp.zeros((self.natm, self.natm, ngr), dtype=float, format='dok') - self._d = None - self._s = sp.zeros((self.natm, ngr), dtype=float, format='dok') - if self._T4 or self._dynamic_T: - self._v = sp.zeros((self.natm, ngr, ngr, ngr, ngr), dtype=float, format='dok') + if orographic_basis == "atmospheric": + self._gh = None + else: + self._gh = sp.zeros((self.natm, self.natm, ngr), dtype=float, format='dok') + self._d = None + self._s = sp.zeros((self.natm, ngr), dtype=float, format='dok') + if self._T4 or self._dynamic_T: + self._v = sp.zeros((self.natm, ngr, ngr, ngr, ngr), dtype=float, format='dok') # s inner products args_list = [[(i, j), self.iip.symbolic_inner_product, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(ngr)] - output = self._p_compute(pool, args_list, subs, self._s, timeout) + output = _parallel_compute(pool, args_list, subs, self._s, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._s = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, ngr, output) else: @@ -357,7 +374,7 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti args_list = [[(i, j, k), self.iip.ip_jac, (self._F(i), self._F(j), self._phi(k))] for i in range(self.natm) for j in range(self.natm) for k in range(ngr)] - output = self._p_compute(pool, args_list, subs, self._gh, timeout) + output = _parallel_compute(pool, args_list, subs, self._gh, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._gh = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, ngr)) else: @@ -369,13 +386,13 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.natm) for j in range(ngr) for k in range(j, ngr) for ell in range(k, ngr) for m in range(ell, ngr)] - output = self._p_compute(pool, args_list, subs, self._v, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not(self.mk_subs)) elif self._dynamic_T: # v inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.natm) for m in range(ngr)] - output = self._p_compute(pool, args_list, subs, self._v, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not(self.mk_subs)) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -397,31 +414,37 @@ def compute_inner_products(self, num_threads=None, timeout=None): If `None` or `False`, no timeout occurs. Default to `None`. """ - - self._a = sp.zeros((self.natm, self.natm), dtype=float, format='dok') - self._u = sp.zeros((self.natm, self.natm), dtype=float, format='dok') - self._c = sp.zeros((self.natm, self.natm), dtype=float, format='dok') - self._b = sp.zeros((self.natm, self.natm, self.natm), dtype=float, format='dok') - self._g = sp.zeros((self.natm, self.natm, self.natm), dtype=float, format='dok') - if self._T4 or self._dynamic_T: - self._z = sp.zeros((self.natm, self.natm, self.natm, self.natm, self.natm), dtype=float, format='dok') + if self.return_symbolic: + self._a = None + self._u = None + self._c = None + self._b = None + self._g = None + self._z = None + else: + self._a = sp.zeros((self.natm, self.natm), dtype=float, format='dok') + self._u = sp.zeros((self.natm, self.natm), dtype=float, format='dok') + self._c = sp.zeros((self.natm, self.natm), dtype=float, format='dok') + self._b = sp.zeros((self.natm, self.natm, self.natm), dtype=float, format='dok') + self._g = sp.zeros((self.natm, self.natm, self.natm), dtype=float, format='dok') + if self._T4 or self._dynamic_T: + self._z = sp.zeros((self.natm, self.natm, self.natm, self.natm, self.natm), dtype=float, format='dok') if self.stored: if num_threads is None: num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - #//TODO: Fix substitution here, temporary fix - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + else: + subs = self.subs # a inner products args_list = [[(i, j), self.ip.ip_lap, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - output = self._p_compute(pool, args_list, subs, self._a, timeout) + output = _parallel_compute(pool, args_list, subs, self._a, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._a = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) else: @@ -431,7 +454,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j), self.ip.symbolic_inner_product, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - output = self._p_compute(pool, args_list, subs, self._u, timeout) + output = _parallel_compute(pool, args_list, subs, self._u, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._u = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) else: @@ -441,7 +464,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j), self.ip.ip_diff_x, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - output = self._p_compute(pool, args_list, subs, self._c, timeout) + output = _parallel_compute(pool, args_list, subs, self._c, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._c = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) else: @@ -451,7 +474,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k), self.ip.ip_jac_lap, (self._F(i), self._F(j), self._F(k))] for i in range(self.natm) for j in range(self.natm) for k in range(self.natm)] - output = self._p_compute(pool, args_list, subs, self._b, timeout) + output = _parallel_compute(pool, args_list, subs, self._b, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._b = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) else: @@ -461,7 +484,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k), self.ip.ip_jac, (self._F(i), self._F(j), self._F(k))] for i in range(self.natm) for j in range(self.natm) for k in range(self.natm)] - output = self._p_compute(pool, args_list, subs, self._g, timeout) + output = _parallel_compute(pool, args_list, subs, self._g, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._g = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) else: @@ -472,13 +495,13 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.natm) for j in range(self.natm) for k in range(j, self.natm) for ell in range(k, self.natm) for m in range(ell, self.natm)] - output = self._p_compute(pool, args_list, subs, self._z, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._z, timeout, permute=True, symbolic_int=not(self.mk_subs)) elif self._dynamic_T: # z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.natm) for m in range(self.natm)] - output = self._p_compute(pool, args_list, subs, self._z, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._z, timeout, permute=True, symbolic_int=not(self.mk_subs)) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -496,7 +519,7 @@ def natm(self): # !-----------------------------------------------------! def _integrate(self, subs, args): - if self.return_symbolic: + if not(self.mk_subs): res = _apply(args) return res[1] @@ -513,10 +536,11 @@ def _integrate(self, subs, args): def a(self, i, j): """Function to compute the matrix of the eigenvalues of the Laplacian (atmospheric): :math:`a_{i, j} = (F_i, {\\nabla}^2 F_j)`.""" if not self.stored: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.ip.ip_lap, (self._F(i), self._F(j)), subs) return self._integrate(subs, args) else: @@ -525,10 +549,11 @@ def a(self, i, j): def u(self, i, j): """Function to compute the matrix of inner product: :math:`u_{i, j} = (F_i, F_j)`.""" if not self.stored: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.ip.symbolic_inner_product, (self._F(i), self._F(j)), subs) return self._integrate(subs, args) else: @@ -537,10 +562,11 @@ def u(self, i, j): def b(self, i, j, k): """Function to compute the tensors holding the Jacobian inner products: :math:`b_{i, j, k} = (F_i, J(F_j, \\nabla^2 F_k))`.""" if not self.stored: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j, k), self.ip.ip_jac_lap, (self._F(i), self._F(j), self._F(k)), subs) return self._integrate(subs, args) else: @@ -549,10 +575,10 @@ def b(self, i, j, k): def c(self, i, j): """Function to compute the matrix of beta terms for the atmosphere: :math:`c_{i,j} = (F_i, \\partial_x F_j)`.""" if not self.stored: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + else: + subs = self.subs args = ((i, j), self.ip.ip_diff_x, (self._F(i), self._F(j)), subs) return self._integrate(subs, args) @@ -562,10 +588,11 @@ def c(self, i, j): def g(self, i, j, k): """Function to compute tensors holding the Jacobian inner products: :math:`g_{i,j,k} = (F_i, J(F_j, F_k))`.""" if not self.stored: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j, k), self.ip.ip_jac, (self._F(i), self._F(j), self._F(k)), subs) return self._integrate(subs, args) else: @@ -578,17 +605,17 @@ def gh(self, i, j, k): if self.stored and self._gh is not None: return self._gh[i, j, k] else: - if self.connected_to_ocean: - extra_subs = self.oceanic_basis.substitutions - elif self.connected_to_ground: - extra_subs = self.ground_basis.substitutions + if self.mk_subs: + if self.connected_to_ocean: + extra_subs = self.oceanic_basis.substitutions + elif self.connected_to_ground: + extra_subs = self.ground_basis.substitutions + else: + extra_subs = None + subs = self.subs + self.atmospheric_basis.substitutions + extra_subs else: - extra_subs = None - - if self.return_symbolic: subs = self.subs - else: - subs = self.subs + self.atmospheric_basis.substitutions + extra_subs + args = ((i, j, k), self.iip.ip_jac, (self._F(i), self._F(j), self._phi(k)), subs) return self._integrate(subs, args) else: @@ -600,17 +627,17 @@ def s(self, i, j): if self.stored and self._s is not None: return self._s[i, j] else: - if self.connected_to_ocean: - extra_subs = self.oceanic_basis.substitutions - elif self.connected_to_ground: - extra_subs = self.ground_basis.substitutions + if self.mk_subs: + if self.connected_to_ocean: + extra_subs = self.oceanic_basis.substitutions + elif self.connected_to_ground: + extra_subs = self.ground_basis.substitutions + else: + extra_subs = None + subs = self.subs + self.atmospheric_basis.substitutions + extra_subs else: - extra_subs = None - - if self.return_symbolic: subs = self.subs - else: - subs = self.subs + self.atmospheric_basis.substitutions + extra_subs + args = ((i, j), self.iip.symbolic_inner_product, (self._F(i), self._phi(j)), subs) return self._integrate(subs, args) else: @@ -622,17 +649,18 @@ def d(self, i, j): if self.stored and self._d is not None: return self._d[i, j] else: - if self.connected_to_ocean: - extra_subs = self.oceanic_basis.substitutions - elif self.connected_to_ground: - extra_subs = self.ground_basis.substitutions - else: - extra_subs = None + if self.mk_subs: + if self.connected_to_ocean: + extra_subs = self.oceanic_basis.substitutions + elif self.connected_to_ground: + extra_subs = self.ground_basis.substitutions + else: + extra_subs = None - if self.return_symbolic: - subs = self.subs - else: subs = self.subs + self.atmospheric_basis.substitutions + extra_subs + else: + subs = self.subs + args = ((i, j), self.iip.ip_lap, (self._F(i), self._phi(j)), subs) return self._integrate(subs, args) else: @@ -641,10 +669,10 @@ def d(self, i, j): def z(self, i, j, k, l, m): """Function to compute the :math:`T^4` temperature forcing for the radiation lost by atmosphere to space & ground/ocean: :math:`z_{i,j,k,l,m} = (F_i, F_j F_k F_l F_m)`.""" if not self.stored: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + else: + subs = self.subs args = ((i, j, k, l, m), self.ip.symbolic_inner_product, (self._F(i), self._F(j) * self._F(k) * self._F(l) * self._F(m)), subs) if self.quadrature: @@ -659,10 +687,11 @@ def z(self, i, j, k, l, m): def v(self, i, j, k, l, m): """Function to compute the :math:`T^4` temperature forcing of the ocean on the atmosphere: :math:`v_{i,j,k,l,m} = (F_i, \\phi_j \\phi_k \\phi_l \\phi_m)`.""" if not self.stored: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j, k, l, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(j) * self._phi(k) * self._phi(l) * self._phi(m)), subs) if self.quadrature: res = _num_apply(args) @@ -736,7 +765,7 @@ class OceanicSymbolicInnerProducts(OceanicInnerProducts): symbolic computation. """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False, make_substitution=True): OceanicInnerProducts.__init__(self) @@ -788,10 +817,15 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.return_symbolic = return_symbolic if return_symbolic: - self._p_compute = _symbolic_compute - self.subs = [('n', params.symbolic_params['n'])] + self.mk_subs = make_substitution + # _parallel_compute = _symbolic_compute + if self.mk_subs: + self.subs = [('n', self.n)] + else: + self.subs = None else: - self._p_compute = _parallel_compute + self.mk_subs = True + # _parallel_compute = _parallel_compute self.subs = [('n', self.n)] if inner_product_definition is None: @@ -847,22 +881,27 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None with Pool(max_workers=num_threads) as pool: #//TODO: Check if this needs to be edited for symbolic ip - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions + else: + subs = self.subs natm = len(atmosphere_basis) - self._K = sp.zeros((self.noc, natm), dtype=float, format='dok') - self._W = sp.zeros((self.noc, natm), dtype=float, format='dok') - if self._T4 or self._dynamic_T: - self._Z = sp.zeros((self.noc, natm, natm, natm, natm), dtype=float, format='dok') + if self.return_symbolic: + self._K = None + self._W = None + self._Z = None + else: + self._K = sp.zeros((self.noc, natm), dtype=float, format='dok') + self._W = sp.zeros((self.noc, natm), dtype=float, format='dok') + if self._T4 or self._dynamic_T: + self._Z = sp.zeros((self.noc, natm, natm, natm, natm), dtype=float, format='dok') # K inner products args_list = [[(i, j), self.iip.ip_lap, (self._phi(i), self._F(j))] for i in range(self.noc) for j in range(natm)] - output = self._p_compute(pool, args_list, subs, self._K, timeout) + output = _parallel_compute(pool, args_list, subs, self._K, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._K = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, natm, output) @@ -873,7 +912,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None args_list = [[(i, j), self.iip.symbolic_inner_product, (self._phi(i), self._F(j))] for i in range(self.noc) for j in range(natm)] - output = self._p_compute(pool, args_list, subs, self._W, timeout) + output = _parallel_compute(pool, args_list, subs, self._W, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._W = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, natm, output) @@ -885,13 +924,13 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.noc) for j in range(natm) for k in range(j, natm) for ell in range(k, natm) for m in range(ell, natm)] - output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not(self.mk_subs)) elif self._dynamic_T: # Z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.noc) for m in range(natm)] - output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not(self.mk_subs)) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -913,29 +952,36 @@ def compute_inner_products(self, num_threads=None, timeout=None): If `None` or `False`, no timeout occurs. Default to `None`. """ - - self._M = sp.zeros((self.noc, self.noc), dtype=float, format='dok') - self._U = sp.zeros((self.noc, self.noc), dtype=float, format='dok') - self._N = sp.zeros((self.noc, self.noc), dtype=float, format='dok') - self._O = sp.zeros((self.noc, self.noc, self.noc), dtype=float, format='dok') - self._C = sp.zeros((self.noc, self.noc, self.noc), dtype=float, format='dok') - if self._T4 or self._dynamic_T: - self._V = sp.zeros((self.noc, self.noc, self.noc, self.noc, self.noc), dtype=float, format='dok') + if self.return_symbolic: + self._M = None + self._U = None + self._N = None + self._O = None + self._C = None + self._V = None + else: + self._M = sp.zeros((self.noc, self.noc), dtype=float, format='dok') + self._U = sp.zeros((self.noc, self.noc), dtype=float, format='dok') + self._N = sp.zeros((self.noc, self.noc), dtype=float, format='dok') + self._O = sp.zeros((self.noc, self.noc, self.noc), dtype=float, format='dok') + self._C = sp.zeros((self.noc, self.noc, self.noc), dtype=float, format='dok') + if self._T4 or self._dynamic_T: + self._V = sp.zeros((self.noc, self.noc, self.noc, self.noc, self.noc), dtype=float, format='dok') if self.stored: if num_threads is None: num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.oceanic_basis.substitutions + else: + subs = self.subs # N inner products args_list = [[(i, j), self.ip.ip_diff_x, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - output = self._p_compute(pool, args_list, subs, self._N, timeout) + output = _parallel_compute(pool, args_list, subs, self._N, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._N = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) else: @@ -944,7 +990,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): # M inner products args_list = [[(i, j), self.ip.ip_lap, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - output = self._p_compute(pool, args_list, subs, self._M, timeout) + output = _parallel_compute(pool, args_list, subs, self._M, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._M = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) else: @@ -953,7 +999,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): # U inner products args_list = [[(i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - output = self._p_compute(pool, args_list, subs, self._U, timeout) + output = _parallel_compute(pool, args_list, subs, self._U, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._U = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) else: @@ -963,7 +1009,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k), self.ip.ip_jac, (self._phi(i), self._phi(j), self._phi(k))] for i in range(self.noc) for j in range(self.noc) for k in range(self.noc)] - output = self._p_compute(pool, args_list, subs, self._O, timeout) + output = _parallel_compute(pool, args_list, subs, self._O, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._O = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) else: @@ -973,7 +1019,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k), self.ip.ip_jac_lap, (self._phi(i), self._phi(j), self._phi(k))] for i in range(self.noc) for j in range(self.noc) for k in range(self.noc)] - output = self._p_compute(pool, args_list, subs, self._C, timeout) + output = _parallel_compute(pool, args_list, subs, self._C, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._C = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) else: @@ -984,13 +1030,13 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.noc) for j in range(self.noc) for k in range(j, self.noc) for ell in range(k, self.noc) for m in range(ell, self.noc)] - output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not(self.mk_subs)) elif self._dynamic_T: # V inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.noc) for m in range(self.noc)] - output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not(self.mk_subs)) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -1022,7 +1068,11 @@ def _integrate(self, subs, args): def M(self, i, j): """Function to compute the forcing of the ocean fields on the ocean: :math:`M_{i,j} = (\\phi_i, \\nabla^2 \\phi_j)`.""" if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.ip.ip_lap, (self._phi(i), self._phi(j)), subs) return self._integrate(subs, args) else: @@ -1031,7 +1081,11 @@ def M(self, i, j): def U(self, i, j): """Function to compute the inner products: :math:`U_{i,j} = (\\phi_i, \\phi_j)`.""" if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j)), subs) return self._integrate(subs, args) else: @@ -1040,7 +1094,11 @@ def U(self, i, j): def N(self, i, j): """Function computing the beta term for the ocean: :math:`N_{i,j} = (\\phi_i, \\partial_x \\phi_j)`.""" if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.ip.ip_diff_x, (self._phi(i), self._phi(j)), subs) return self._integrate(subs, args) else: @@ -1049,7 +1107,11 @@ def N(self, i, j): def O(self, i, j, k): """Function to compute the temperature advection term (passive scalar): :math:`O_{i,j,k} = (\\phi_i, J(\\phi_j, \\phi_k))`""" if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + else: + subs = self.subs + args = ((i, j, k), self.ip.ip_jac, (self._phi(i), self._phi(j), self._phi(k)), subs) return self._integrate(subs, args) else: @@ -1058,7 +1120,11 @@ def O(self, i, j, k): def C(self, i, j, k): """Function to compute the tensors holding the Jacobian inner products: :math:`C_{i,j,k} = (\\phi_i, J(\\phi_j,\\nabla^2 \\phi_k))`.""" if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + else: + subs = self.subs + args = ((i, j, k), self.ip.ip_jac_lap, (self._phi(i), self._phi(j), self._phi(k)), subs) return self._integrate(subs, args) else: @@ -1068,7 +1134,11 @@ def K(self, i, j): """Function to commpute the forcing of the ocean by the atmosphere: :math:`K_{i,j} = (\\phi_i, \\nabla^2 F_j)`.""" if self.connected_to_atmosphere: if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.iip.ip_lap, (self._phi(i), self._F(j)), subs) return self._integrate(subs, args) else: @@ -1080,7 +1150,11 @@ def W(self, i, j): """Function to compute the short-wave radiative forcing of the ocean: :math:`W_{i,j} = (\\phi_i, F_j)`.""" if self.connected_to_atmosphere: if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.iip.symbolic_inner_product, (self._phi(i), self._F(j)), subs) return self._integrate(subs, args) else: @@ -1091,7 +1165,11 @@ def W(self, i, j): def V(self, i, j, k, l, m): """Function to compute the :math:`T^4` temperature forcing from the ocean to the atmosphere: :math:`V_{i,j,k,l,m} = (\\phi_i, \\phi_j, \\phi_k, \\phi_l, \\phi_m)`.""" if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + else: + subs = self.subs + args = ((i, j, k, l, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j) * self._phi(k) * self._phi(l) * self._phi(m)), subs) return self._integrate(subs, args) else: @@ -1100,7 +1178,11 @@ def V(self, i, j, k, l, m): def Z(self, i, j, k, l, m): """Function to compute the :math:`T^4` temperature forcing from the atmosphere to the ocean: :math:`Z_{i,j,k,l,m} = (\\phi_i, F_j, F_k, F_l, F_m)`.""" if not self.stored: - subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions + if self.mk_subs: + subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j, k, l, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(j) * self._F(k) * self._F(l) * self._F(m)), subs) return self._integrate(subs, args) else: @@ -1169,7 +1251,7 @@ class GroundSymbolicInnerProducts(GroundInnerProducts): """ def __init__(self, params=None, stored=True, inner_product_definition=None, interaction_inner_product_definition=None, - num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False): + num_threads=None, quadrature=True, timeout=None, dynTinnerproducts=None, T4innerproducts=None, return_symbolic=False, make_substitution=True): GroundInnerProducts.__init__(self) @@ -1221,10 +1303,15 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.return_symbolic = return_symbolic if return_symbolic: - self._p_compute = _symbolic_compute - self.subs = [('n', params.symbolic_params['n'])] + self.mk_subs = make_substitution + # _parallel_compute = _symbolic_compute + if self.mk_subs: + self.subs = [('n', self.n)] + else: + self.subs = None else: - self._p_compute = _parallel_compute + self.mk_subs = True + # _parallel_compute = _parallel_compute self.subs = [('n', self.n)] if inner_product_definition is None: @@ -1278,21 +1365,24 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - #//TODO: Check if this needs changing for symbolic IP. - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.ground_basis.substitutions + self.atmospheric_basis.substitutions + else: + subs = self.subs natm = len(atmosphere_basis) - self._W = sp.zeros((self.ngr, natm), dtype=float, format='dok') - if self._T4 or self._dynamic_T: - self._Z = sp.zeros((self.ngr, natm, natm, natm, natm), dtype=float, format='dok') + if self.return_symbolic: + self._W = None + self._Z = None + else: + self._W = sp.zeros((self.ngr, natm), dtype=float, format='dok') + if self._T4 or self._dynamic_T: + self._Z = sp.zeros((self.ngr, natm, natm, natm, natm), dtype=float, format='dok') # W inner products args_list = [[(i, j), self.iip.symbolic_inner_product, (self._phi(i), self._F(j))] for i in range(self.ngr) for j in range(natm)] - output = self._p_compute(pool, args_list, subs, self._W, timeout) + output = _parallel_compute(pool, args_list, subs, self._W, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._W = sy.matrices.immutable.ImmutableSparseMatrix(self.ngr, natm, output) else: @@ -1303,13 +1393,13 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.ngr) for j in range(natm) for k in range(j, natm) for ell in range(k, natm) for m in range(ell, natm)] - output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not(self.mk_subs)) elif self._dynamic_T: # Z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.ngr) for m in range(natm)] - output = self._p_compute(pool, args_list, subs, self._Z, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not(self.mk_subs)) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -1331,24 +1421,27 @@ def compute_inner_products(self, num_threads=None, timeout=None): If `None` or `False`, no timeout occurs. Default to `None`. """ - - self._U = sp.zeros((self.ngr, self.ngr), dtype=float, format='dok') - if self._T4 or self._dynamic_T: - self._V = sp.zeros((self.ngr, self.ngr, self.ngr, self.ngr, self.ngr), dtype=float, format='dok') + if self.return_symbolic: + self._U = None + self._V = None + else: + self._U = sp.zeros((self.ngr, self.ngr), dtype=float, format='dok') + if self._T4 or self._dynamic_T: + self._V = sp.zeros((self.ngr, self.ngr, self.ngr, self.ngr, self.ngr), dtype=float, format='dok') if self.stored: if num_threads is None: num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - if self.return_symbolic: - subs = self.subs - else: + if self.mk_subs: subs = self.subs + self.ground_basis.substitutions + else: + subs = self.subs # U inner products args_list = [[(i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j))] for i in range(self.ngr) for j in range(self.ngr)] - output = self._p_compute(pool, args_list, subs, self._U, timeout) + output = _parallel_compute(pool, args_list, subs, self._U, timeout, symbolic_int=not(self.mk_subs)) if self.return_symbolic: self._U = sy.matrices.immutable.ImmutableSparseMatrix(self.ngr, self.ngr, output) else: @@ -1359,14 +1452,14 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.ngr) for j in range(self.ngr) for k in range(j, self.ngr) for ell in range(k, self.ngr) for m in range(ell, self.ngr)] - output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not(self.mk_subs)) elif self._dynamic_T: # V inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.ngr) for m in range(self.ngr)] - output = self._p_compute(pool, args_list, subs, self._V, timeout, permute=True) + output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not(self.mk_subs)) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -1416,7 +1509,11 @@ def M(self, i, j): def U(self, i, j): """Function to compute the inner products: :math:`U_{i,j} = (\\phi_i, \\phi_j)`.""" if not self.stored: - subs = self.subs + self.ground_basis.substitutions + if self.mk_subs: + subs = self.subs + self.ground_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j)), subs) return self._integrate(subs, args) else: @@ -1453,7 +1550,11 @@ def W(self, i, j): """Function to compute the short-wave radiative forcing of the ground: :math:`W_{i,j} = (\\phi_i, F_j)`.""" if self.connected_to_atmosphere: if not self.stored: - subs = self.subs + self.ground_basis.substitutions + self.atmospheric_basis.substitutions + if self.mk_subs: + subs = self.subs + self.ground_basis.substitutions + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j), self.iip.symbolic_inner_product, (self._phi(i), self._F(j)), subs) return self._integrate(subs, args) else: @@ -1464,7 +1565,11 @@ def W(self, i, j): def V(self, i, j, k, l, m): """Function to compute the :math:`T^4` temperature forcing from the ground to the atmosphere: :math:`V_{i,j,k,l,m} = (\\phi_i, \\phi_j, \\phi_k, \\phi_l, \\phi_m)`.""" if not self.stored: - subs = self.subs + self.ground_basis.substitutions + if self.mk_subs: + subs = self.subs + self.ground_basis.substitutions + else: + subs = self.subs + args = ((i, j, k, l, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j) * self._phi(k) * self._phi(l) * self._phi(m)), subs) return self._integrate(subs, args) else: @@ -1473,7 +1578,11 @@ def V(self, i, j, k, l, m): def Z(self, i, j, k, l, m): """Function to compute the :math:`T^4` temperature forcing from the atmosphere to the ground: :math:`Z_{i,j,k,l,m} = (\\phi_i, F_j, F_k, F_l, F_m)`.""" if not self.stored: - subs = self.subs + self.ground_basis.substitutions + self.atmospheric_basis.substitutions + if self.mk_subs: + subs = self.subs + self.ground_basis.substitutions + self.atmospheric_basis.substitutions + else: + subs = self.subs + args = ((i, j, k, l, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(j) * self._F(k) * self._F(l) * self._F(m)), subs) return self._integrate(subs, args) else: @@ -1536,20 +1645,36 @@ def _num_apply(ls): return ls[0], res[0] -def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False): +def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False, symbolic_int=False): + if destination is None: + return_dict = True + destination = dict() + else: + return_dict = False - if timeout is False: + if timeout is False or symbolic_int: timeout = None if timeout is not True: - future = pool.map(_apply, args_list, timeout=None) + future = pool.map(_apply, args_list, timeout=timeout) results = future.result() num_args_list = list() i = 0 while True: try: res = next(results) - destination[res[0]] = float(res[1].subs(subs)) + if symbolic_int: + expr = res[1].simplify() + destination[res[0]] = expr + if permute: + i = res[0][0] + idx = res[0][1:] + perm_idx = multiset_permutations(idx) + for perm in perm_idx: + idx = [i] + perm + destination[tuple(idx)] = expr + else: + destination[res[0]] = float(res[1].subs(subs)) except StopIteration: break except TimeoutError: @@ -1580,6 +1705,9 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False except StopIteration: break + if return_dict: + return destination + def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False): result_list = dict() #//TODO: This function needs checking, I have stripped alot of the funcitionality out of _parallell_compute diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 7880ad1..eb4358c 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -5,15 +5,11 @@ This module computes and holds the symbolic representation of the tensors representing the tendencies of the model's equations. """ -from contextlib import redirect_stdout - -from qgs.functions.tendencies import create_tendencies from qgs.functions.symbolic_mul import _add_to_dict, _symbolic_tensordot import numpy as np import sparse as sp import sympy as sy -import warnings import pickle #//TODO: Check non stored IP version of this @@ -57,7 +53,7 @@ class SymbolicTensorLinear(object): The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. """ - def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None, numerically_test_tensor=True): + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None, ): self.atmospheric_inner_products = atmospheric_inner_products self.oceanic_inner_products = oceanic_inner_products @@ -69,8 +65,6 @@ def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_p self.tensor = None self.jacobian_tensor = None - self.test_tensor = numerically_test_tensor - self.params.symbolic_insolation_array() self.params.symbolic_orography_array() @@ -743,10 +737,6 @@ def _set_tensor(self, dic, set_symbolic=False): if set_symbolic: self._set_symbolic_tensor() - - if self.test_tensor: - print("Testing Tensor Numerically") - self.test_tensor_numerically(self.tensor_dic) def _set_symbolic_tensor(self): ndim = self.params.ndim @@ -879,57 +869,6 @@ def subs_tensor(self, tensor=None, dict_opp=True, variables=None, remain_variabl ten_out = self.tensor.subs(symbol_to_number_map) return ten_out - - def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): - """ - Uses sympy substitution to convert the symbolic tensor, or symbolic dictionary, to a numerical one. - This is then compared to the tensor calculated by the qgs.tensor.symbolic module. - - """ - ndim = self.params.ndim - - if self.params.dynamic_T: - if self.params.T4: - #//TODO: Make a proper error message for here - raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") - else: - dims = (ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1) - else: - dims = (ndim + 1, ndim + 1, ndim + 1) - - _, _, numerical_tensor = create_tendencies(self.params, return_qgtensor=True) - - if tensor is None: - if dict_opp: - tensor = self.tensor_dic - else: - tensor = self.tensor - - subbed_ten = self.subs_tensor(tensor) - if isinstance(subbed_ten, dict): - coords = np.array([list(k) for k in subbed_ten.keys()]).T - data = np.array(list(subbed_ten.values()), dtype=float) - subbed_tensor_sp = sp.COO(coords, data, shape=dims) - else: - subbed_ten = np.array(subbed_ten) - subbed_tensor_np = np.array(subbed_ten).astype(np.float64) - subbed_tensor_sp = sp.COO.from_numpy(subbed_tensor_np) - - diff_arr = subbed_tensor_sp.todense() - numerical_tensor.tensor.todense() - - #TODO: Is there a better way to do error/pass messages? - total_error = np.sum(np.abs(diff_arr)) - max_error = np.max(np.abs(diff_arr)) - - if max_error > tol: - self.print_tensor(diff_arr, tol=tol) - - raise ValueError("Symbolic tensor and numerical tensor do not match at the above coordinates, with a total error of: " + str(total_error)) - - elif total_error > tol: - warnings.warn("Symbolic tensor and numerical tensor have a combined error of: " + str(total_error)) - else: - print("Tensor passes numerical test with a combined error of less than: " + str(tol)) def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): if tensor is None: @@ -998,9 +937,9 @@ class SymbolicTensorDynamicT(SymbolicTensorLinear): The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. """ - def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None, numerically_test_tensor=True): + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): - SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products, numerically_test_tensor) + SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) if params.dynamic_T: self.compute_tensor() @@ -1357,9 +1296,9 @@ class SymbolicTensorT4(SymbolicTensorLinear): The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. """ - def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None, numerically_test_tensor=True): + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): - SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products, numerically_test_tensor) + SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) if params.T4: self.compute_tensor() From bc49dc539851274f3b64cf5acf89c8ef5846b024 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 18 Aug 2023 17:23:09 +0200 Subject: [PATCH 043/143] Tests added for symbolic version --- model_test/test_aotensor_sym.py | 151 +++++++++++++++++++++++++++ model_test/test_aotensor_sym_dynT.py | 95 +++++++++++++++++ model_test/test_base_symbolic.py | 97 +++++++++++++++++ 3 files changed, 343 insertions(+) create mode 100644 model_test/test_aotensor_sym.py create mode 100644 model_test/test_aotensor_sym_dynT.py create mode 100644 model_test/test_base_symbolic.py diff --git a/model_test/test_aotensor_sym.py b/model_test/test_aotensor_sym.py new file mode 100644 index 0000000..87bdddb --- /dev/null +++ b/model_test/test_aotensor_sym.py @@ -0,0 +1,151 @@ + +import sys +import os + +path = os.path.abspath('./') +base = os.path.basename(path) +if base == 'model_test': + sys.path.extend([os.path.abspath('../')]) +else: + sys.path.extend([path]) + + +import unittest +import numpy as np + +from qgs.params.params import QgParams +from qgs.inner_products import symbolic +from qgs.tensors.qgtensor import QgsTensor +from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear + +from model_test.test_base_symbolic import TestBaseSymbolic + +real_eps = np.finfo(np.float64).eps + +class TestSymbolicAOTensor(TestBaseSymbolic): + ''' + Test class for the Linear Symbolic Tensor + The tensor is tested against the reference file, and then the numerical tensor calculated in qgs. + ''' + + filename = 'test_aotensor.ref' + + def test_sym_against_ref(self): + self.check_lists() + + def test_sym_against_num(self): + self.check_numerical_lists() + + def symbolic_outputs(self, output_func=None): + + if output_func is None: + self.symbolic_values.clear() + tfunc = self.save_ip_symbolic + else: + tfunc = output_func + + params = QgParams({'rr': 287.e0, 'sb': 5.6e-8}) + params.set_atmospheric_channel_fourier_modes(2, 2, mode="symbolic") + params.set_oceanic_basin_fourier_modes(2, 4, mode="symbolic") + + # Setting MAOOAM default parameters + params.set_params({'kd': 0.04, 'kdp': 0.04, 'n': 1.5}) + + aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) + oip = symbolic.OceanicSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) + + aip.connect_to_ocean(oip) + + sym_aotensor = SymbolicTensorLinear(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) + + subbed_tensor = sym_aotensor.sub_tensor() + + for coo, val in zip(subbed_tensor.keys(), subbed_tensor.values()): + _ip_string_format(tfunc, 'sym_aotensor', coo, val) + + def numerical_outputs(self, output_func=None): + + if output_func is None: + self.numerical_values.clear() + tfunc = self.save_ip_numeric + else: + tfunc = output_func + + params = QgParams({'rr': 287.e0, 'sb': 5.6e-8}) + params.set_atmospheric_channel_fourier_modes(2, 2, mode="symbolic") + params.set_oceanic_basin_fourier_modes(2, 4, mode="symbolic") + + # Setting MAOOAM default parameters + params.set_params({'kd': 0.04, 'kdp': 0.04, 'n': 1.5}) + + aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=False) + oip = symbolic.OceanicSymbolicInnerProducts(params, return_symbolic=False) + + aip.connect_to_ocean(oip) + + num_aotensor = QgsTensor(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) + + for coo, val in zip(num_aotensor.tensor.coords.T, num_aotensor.tensor.data): + _ip_string_format(tfunc, 'num_aotensor', coo, val) + +def _ip_string_format(func, symbol, indices, value): + if abs(value) >= real_eps: + s = symbol + for i in indices: + s += "["+str(i)+"]" + s += " = % .5E" % value + func(s) + +if __name__ == "__main__": + unittest.main() + +# def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): +# """ +# Uses sympy substitution to convert the symbolic tensor, or symbolic dictionary, to a numerical one. +# This is then compared to the tensor calculated by the qgs.tensor.symbolic module. + +# """ +# ndim = self.params.ndim + +# if self.params.dynamic_T: +# if self.params.T4: + +# raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") +# else: +# dims = (ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1) +# else: +# dims = (ndim + 1, ndim + 1, ndim + 1) + +# _, _, numerical_tensor = create_tendencies(self.params, return_qgtensor=True) + +# if tensor is None: +# if dict_opp: +# tensor = self.tensor_dic +# else: +# tensor = self.tensor + +# subbed_ten = self.subs_tensor(tensor) +# if isinstance(subbed_ten, dict): +# coords = np.array([list(k) for k in subbed_ten.keys()]).T +# data = np.array(list(subbed_ten.values()), dtype=float) +# subbed_tensor_sp = sp.COO(coords, data, shape=dims) +# else: +# subbed_ten = np.array(subbed_ten) +# subbed_tensor_np = np.array(subbed_ten).astype(np.float64) +# subbed_tensor_sp = sp.COO.from_numpy(subbed_tensor_np) + +# diff_arr = subbed_tensor_sp.todense() - numerical_tensor.tensor.todense() + + +# total_error = np.sum(np.abs(diff_arr)) +# max_error = np.max(np.abs(diff_arr)) + +# if max_error > tol: +# self.print_tensor(diff_arr, tol=tol) + +# raise ValueError("Symbolic tensor and numerical tensor do not match at the above coordinates, with a total error of: " + str(total_error)) + +# elif total_error > tol: +# warnings.warn("Symbolic tensor and numerical tensor have a combined error of: " + str(total_error)) +# else: +# print("Tensor passes numerical test with a combined error of less than: " + str(tol)) \ No newline at end of file diff --git a/model_test/test_aotensor_sym_dynT.py b/model_test/test_aotensor_sym_dynT.py new file mode 100644 index 0000000..9140d90 --- /dev/null +++ b/model_test/test_aotensor_sym_dynT.py @@ -0,0 +1,95 @@ + +import sys +import os + +path = os.path.abspath('./') +base = os.path.basename(path) +if base == 'model_test': + sys.path.extend([os.path.abspath('../')]) +else: + sys.path.extend([path]) + + +import unittest +import numpy as np + +from qgs.params.params import QgParams +from qgs.inner_products import symbolic +from qgs.tensors.qgtensor import QgsTensorDynamicT +from qgs.tensors.symbolic_qgtensor import SymbolicTensorDynamicT + +from model_test.test_base_symbolic import TestBaseSymbolic + +real_eps = np.finfo(np.float64).eps + +class TestSymbolicAOTensorDynT(TestBaseSymbolic): + ''' + Test class for the Dynamic T Symbolic Tensor + The tensor is tested against the reference file, and then the numerical tensor calculated in qgs. + ''' + + def test_sym_against_num(self): + self.check_numerical_lists_flt() + + def symbolic_outputs(self, output_func=None): + + if output_func is None: + self.symbolic_values.clear() + tfunc = self.save_ip_symbolic + else: + tfunc = output_func + + params = QgParams({'rr': 287.e0, 'sb': 5.6e-8}, dynamic_T=True) + params.set_atmospheric_channel_fourier_modes(2, 2, mode="symbolic") + params.set_oceanic_basin_fourier_modes(2, 4, mode="symbolic") + + # Setting MAOOAM default parameters + params.set_params({'kd': 0.04, 'kdp': 0.04, 'n': 1.5}) + + aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) + oip = symbolic.OceanicSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) + + aip.connect_to_ocean(oip) + + sym_aotensor = SymbolicTensorDynamicT(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) + + subbed_tensor = sym_aotensor.sub_tensor() + + for coo, val in zip(subbed_tensor.keys(), subbed_tensor.values()): + _ip_string_format(tfunc, 'sym_aotensor', coo, val) + + def numerical_outputs(self, output_func=None): + + if output_func is None: + self.numerical_values.clear() + tfunc = self.save_ip_numeric + else: + tfunc = output_func + + params = QgParams({'rr': 287.e0, 'sb': 5.6e-8}, dynamic_T=True) + params.set_atmospheric_channel_fourier_modes(2, 2, mode="symbolic") + params.set_oceanic_basin_fourier_modes(2, 4, mode="symbolic") + + # Setting MAOOAM default parameters + params.set_params({'kd': 0.04, 'kdp': 0.04, 'n': 1.5}) + + aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=False) + oip = symbolic.OceanicSymbolicInnerProducts(params, return_symbolic=False) + + aip.connect_to_ocean(oip) + + num_aotensor = QgsTensorDynamicT(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) + + for coo, val in zip(num_aotensor.tensor.coords.T, num_aotensor.tensor.data): + _ip_string_format(tfunc, 'num_aotensor', coo, val) + +def _ip_string_format(func, symbol, indices, value): + if abs(value) >= real_eps: + s = symbol + for i in indices: + s += "["+str(i)+"]" + s += " = % .5E" % value + func(s) + +if __name__ == "__main__": + unittest.main() diff --git a/model_test/test_base_symbolic.py b/model_test/test_base_symbolic.py new file mode 100644 index 0000000..38de7ac --- /dev/null +++ b/model_test/test_base_symbolic.py @@ -0,0 +1,97 @@ + +import os + +path = os.path.abspath('./') +base = os.path.basename(path) +if base == 'model_test': + fold = "" +else: + fold = 'model_test/' + +import unittest +import numpy as np + +real_eps = np.finfo(np.float64).eps + +class TestBaseSymbolic(unittest.TestCase): + + reference = list() + symbolic_values = list() + numerical_values = list() + folder = fold + + def load_ref_from_file(self): + self.reference.clear() + f = open(self.folder+self.filename, 'r') + buf = f.readlines() + + for l in buf: + self.reference.append(l[:-1]) + + f.close() + + def save_ip_symbolic(self, s): + self.symbolic_values.append(s) + + def save_ip_numeric(self, s): + self.numerical_values.append(s) + + def check_lists_flt(self): + if len(self.symbolic_values) == 0: + self.symbolic_outputs() + self.load_ref_from_file() + for v, r in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.reference)))): + self.assertTrue(self.match_flt(v, r), msg=v+' != '+r+' !!!') + + def check_lists(self, cmax=1): + if len(self.symbolic_values) == 0: + self.symbolic_outputs() + self.load_ref_from_file() + for v, r in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.reference)))): + self.assertTrue(self.match_str(v, r, cmax), msg=v+' != '+r+' !!!') + + def check_numerical_lists_flt(self): + if len(self.symbolic_values) == 0: + self.symbolic_outputs() + self.numerical_outputs() + for v, r in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.reference)))): + self.assertTrue(self.match_flt(v, r), msg=v+' != '+r+' !!!') + + def check_numerical_lists(self, cmax=1): + if len(self.symbolic_values) == 0: + self.symbolic_outputs() + self.numerical_outputs() + for v, r, in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.numerical_values)))): + self.assertTrue(self.match_str(v, r, cmax), msg=v+' != '+r+' !!!') + + def symbolic_outputs(self): + pass + + def numerical_outputs(self): + pass + + @staticmethod + def match_flt(s1, s2, eps=real_eps): + + s1p = s1.split('=') + s2p = s2.split('=') + + v1 = float(s1p[1]) + v2 = float(s2p[1]) + print(v1, v2, eps) + + return abs(v1 - v2) < eps + + @staticmethod + def match_str(s1, s2, cmax=1): + + c = 0 + + for c1, c2 in zip(s1, s2): + if c1 != c2: + c += 1 + + if c > cmax: + return False + + return True From 7317705a2c8a24fb1b536c3e2c37f669b4f5d4ff Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 18 Aug 2023 17:50:51 +0200 Subject: [PATCH 044/143] Minor edits to structure of outputs --- .../Symbolic Output-Linear-groud test.ipynb | 491 ++++++++++++++++++ notebooks/Symbolic Output-Linear.ipynb | 175 ++----- notebooks/Symbolic Output-dynamic_T.ipynb | 285 ++++------ qgs/functions/symbolic_output.py | 2 +- qgs/tensors/symbolic_qgtensor.py | 24 +- 5 files changed, 641 insertions(+), 336 deletions(-) create mode 100644 notebooks/Symbolic Output-Linear-groud test.ipynb diff --git a/notebooks/Symbolic Output-Linear-groud test.ipynb b/notebooks/Symbolic Output-Linear-groud test.ipynb new file mode 100644 index 0000000..f20eb3a --- /dev/null +++ b/notebooks/Symbolic Output-Linear-groud test.ipynb @@ -0,0 +1,491 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "037e82a6", + "metadata": {}, + "source": [ + "# Linear symbolic Land-atmosphere example " + ] + }, + { + "cell_type": "markdown", + "id": "57e1a1bb", + "metadata": {}, + "source": [ + "Testing platform for the symbolic equation version of the qgs model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12f59a3f", + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "sys.path.extend([os.path.abspath('../')])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b3814d5", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import sympy as sy\n", + "import sparse as sp\n", + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7711e102", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.params.params import QgParams\n", + "from qgs.functions.tendencies import create_tendencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8500ea89", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, GroundSymbolicInnerProducts\n", + "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", + "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", + "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf632fac", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters = QgParams({'phi0_npi': np.deg2rad(50.)/np.pi, 'n':1.3 }, dynamic_T=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2a8e7c1", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", + "# Mode truncation at the wavenumber 2 in the x and at the \n", + "# wavenumber 4 in the y spatial coordinates for the ocean\n", + "model_parameters.set_ground_channel_fourier_modes(2, 2, mode=\"symbolic\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3237dc76", + "metadata": {}, + "outputs": [], + "source": [ + "# Changing (increasing) the orography depth\n", + "model_parameters.ground_params.set_orography(0.2, 1)\n", + "# Setting the parameters of the heat transfer from the soil\n", + "model_parameters.gotemperature_params.set_params({'gamma': 1.6e7, 'T0': 300})\n", + "model_parameters.atemperature_params.set_params({ 'hlambda':10, 'T0': 290})\n", + "# Setting atmospheric parameters\n", + "model_parameters.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.085, 'kdp': 0.02})\n", + "\n", + "# Setting insolation \n", + "model_parameters.gotemperature_params.set_params({})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51907767", + "metadata": {}, + "outputs": [], + "source": [ + "C_g = 300\n", + "model_parameters.atemperature_params.set_insolation(0.4*C_g , 0)\n", + "\n", + "model_parameters.gotemperature_params.set_insolation(C_g , 0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6112c83a", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "# Takes ~1 mins\n", + "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=False) # <- Can be turned off once saved" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "805e53eb", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=True) # <- Can be turned off once saved" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea85360f", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True) \n", + "gnd_loaded = GroundSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "196d5440", + "metadata": {}, + "outputs": [], + "source": [ + "atm_loaded.load_from_file('temp_atm_sym_lin_gnd_test.ip')\n", + "gnd_loaded.load_from_file('temp_gnd_sym_lin_gnd_test.ip')" + ] + }, + { + "cell_type": "markdown", + "id": "d43d341b", + "metadata": {}, + "source": [ + "## Outputting model equations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f67ffc01", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations" + ] + }, + { + "cell_type": "markdown", + "id": "8ddc3286", + "metadata": {}, + "source": [ + "Calculating the functions and tensor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9479fcb0", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "funcs, = create_symbolic_equations(model_parameters, continuation_variables={'atm_C', 'atm_C_val', 'go_C', 'go_C_val'})" + ] + }, + { + "cell_type": "markdown", + "id": "0c185e14", + "metadata": {}, + "source": [ + "Convert the dictionary of functions to a lambdified function (not working with numba)\n", + "The input `remain_variables` specifies which variables not to substitute with values." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0fb36604", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "print(funcs)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ec572eb", + "metadata": {}, + "outputs": [], + "source": [ + "from numba import njit" + ] + }, + { + "cell_type": "markdown", + "id": "2bd791a9", + "metadata": {}, + "source": [ + "Below function has the **kwargs input removed as I cannot get this to work with numba" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5d98d6b", + "metadata": {}, + "outputs": [], + "source": [ + "@njit\n", + "def f(t, U):\n", + "\t#Tendency function of the qgs model\n", + "\tF = np.empty_like(U)\n", + "\tC_go = 300\n", + "\tC_a = 0.4 * C_go\n", + "\tF[0] = -0.0425*U[0] + 0.0425*U[10] - 0.156054828133898*U[12] + 0.156054828133898*U[2]\n", + "\tF[1] = -0.980418808722262*U[0]*U[2] - 0.980418808722262*U[10]*U[12] + 0.0425*U[11] - 1.56867009395562*U[13]*U[15] - 1.50055762081784*U[14]*U[17] + 1.50055762081784*U[15]*U[16] - 0.0425*U[1] + 0.101317695204044*U[2] - 1.56867009395562*U[3]*U[5] - 1.50055762081784*U[4]*U[7] + 1.50055762081784*U[5]*U[6]\n", + "\tF[2] = 0.980418808722262*U[0]*U[1] - 0.0580129472616723*U[0] + 0.980418808722262*U[10]*U[11] + 0.0580129472616723*U[10] + 0.0425*U[12] + 1.56867009395562*U[13]*U[14] + 1.50055762081784*U[14]*U[16] + 1.50055762081784*U[15]*U[17] - 0.101317695204044*U[1] - 0.0425*U[2] + 1.56867009395562*U[3]*U[4] + 1.50055762081784*U[4]*U[6] + 1.50055762081784*U[5]*U[7]\n", + "\tF[3] = 1.87265793760678*U[11]*U[15] - 1.87265793760678*U[12]*U[14] + 0.0425*U[13] - 0.0624219312535594*U[15] + 3.74531587521356*U[16]*U[19] - 3.74531587521356*U[17]*U[18] + 1.87265793760678*U[1]*U[5] - 1.87265793760678*U[2]*U[4] - 0.0425*U[3] + 0.0624219312535594*U[5] + 3.74531587521356*U[6]*U[9] - 3.74531587521356*U[7]*U[8]\n", + "\tF[4] = -1.02902937637678*U[0]*U[5] - 1.02902937637678*U[10]*U[15] + 1.73752196836555*U[11]*U[17] + 0.574852231579351*U[12]*U[13] - 1.73752196836555*U[12]*U[16] + 0.0425*U[14] - 0.0342706502636204*U[17] + 1.73752196836555*U[1]*U[7] + 0.574852231579351*U[2]*U[3] - 1.73752196836555*U[2]*U[6] - 0.0425*U[4] + 0.0478988752370612*U[5] + 0.0342706502636204*U[7]\n", + "\tF[5] = 1.02902937637678*U[0]*U[4] + 1.02902937637678*U[10]*U[14] - 0.574852231579351*U[11]*U[13] - 1.73752196836555*U[11]*U[16] - 1.73752196836555*U[12]*U[17] + 0.0438818497388818*U[13] + 0.0425*U[15] + 0.0342706502636204*U[16] - 0.574852231579351*U[1]*U[3] - 1.73752196836555*U[1]*U[6] - 1.73752196836555*U[2]*U[7] - 0.0438818497388818*U[3] - 0.0478988752370612*U[4] - 0.0425*U[5] - 0.0342706502636204*U[6]\n", + "\tF[6] = -2.71889339738442*U[0]*U[7] - 2.71889339738442*U[10]*U[17] + 0.753865979381443*U[11]*U[15] + 0.753865979381443*U[12]*U[14] - 4.35022943581506*U[13]*U[19] - 0.0251288659793814*U[15] + 0.0425*U[16] + 0.753865979381443*U[1]*U[5] + 0.753865979381443*U[2]*U[4] - 4.35022943581506*U[3]*U[9] + 0.0251288659793814*U[5] - 0.0425*U[6] + 0.0702434536337315*U[7]\n", + "\tF[7] = 2.71889339738442*U[0]*U[6] + 2.71889339738442*U[10]*U[16] - 0.753865979381443*U[11]*U[14] + 0.753865979381443*U[12]*U[15] + 4.35022943581506*U[13]*U[18] + 0.0251288659793814*U[14] + 0.0425*U[17] - 0.753865979381443*U[1]*U[4] + 0.753865979381443*U[2]*U[5] + 4.35022943581506*U[3]*U[8] - 0.0251288659793814*U[4] - 0.0702434536337315*U[6] - 0.0425*U[7]\n", + "\tF[8] = -2.26482546109569*U[0]*U[9] - 2.26482546109569*U[10]*U[19] - 1.7450294536311*U[13]*U[17] + 0.0425*U[18] - 1.7450294536311*U[3]*U[7] - 0.0425*U[8] + 0.050658847602022*U[9]\n", + "\tF[9] = 2.26482546109569*U[0]*U[8] + 2.26482546109569*U[10]*U[18] + 1.7450294536311*U[13]*U[16] + 0.0425*U[19] + 1.7450294536311*U[3]*U[6] - 0.050658847602022*U[8] - 0.0425*U[9]\n", + "\tF[10] = 4.68670500616653e-6*C_a + 0.00386363636363636*U[0] - 0.023715438957012*U[10] - 1.41868025576271*U[11]*U[2] + 1.41868025576271*U[12]*U[1] + 0.0141868025576271*U[12] - 1.13494420461017*U[14]*U[5] + 1.13494420461017*U[15]*U[4] - 2.83736051152543*U[16]*U[7] + 2.83736051152543*U[17]*U[6] - 2.26988840922034*U[18]*U[9] + 2.26988840922034*U[19]*U[8] + 0.00645434108527132*U[20] - 0.0141868025576271*U[2]\n", + "\tF[11] = -1.43757363347933*U[0]*U[12] + 1.02191932371371*U[10]*U[2] - 0.0315441157231782*U[11] + 0.0214771158470353*U[12] + 1.63507091794193*U[13]*U[5] + 1.21855791962175*U[14]*U[7] - 2.30011781356693*U[15]*U[3] - 1.21855791962175*U[15]*U[6] + 1.85472813238771*U[16]*U[5] - 1.85472813238771*U[17]*U[4] + 0.00900906225374311*U[1] + 0.00559477950653936*U[21]\n", + "\tF[12] = 1.43757363347933*U[0]*U[11] + 0.0122974647859652*U[0] - 1.02191932371371*U[10]*U[1] - 0.0122974647859652*U[10] - 0.0214771158470353*U[11] - 0.0315441157231782*U[12] - 1.63507091794193*U[13]*U[4] + 2.30011781356693*U[14]*U[3] - 1.21855791962175*U[14]*U[6] - 1.21855791962175*U[15]*U[7] + 1.85472813238771*U[16]*U[4] + 1.85472813238771*U[17]*U[5] + 0.00559477950653936*U[22] + 0.00900906225374311*U[2]\n", + "\tF[13] = -1.24843862507119*U[11]*U[5] + 1.24843862507119*U[12]*U[4] - 0.0363121306090808*U[13] - 2.3185288751322*U[14]*U[2] + 2.3185288751322*U[15]*U[1] + 0.017834837501017*U[15] - 2.49687725014237*U[16]*U[9] + 2.49687725014237*U[17]*U[8] - 4.63705775026441*U[18]*U[7] + 4.63705775026441*U[19]*U[6] + 0.00507126799557032*U[23] + 0.0121428571428571*U[3] - 0.017834837501017*U[5]\n", + "\tF[14] = -1.16886956037576*U[0]*U[15] + 0.422511733532696*U[10]*U[5] - 0.612715105162524*U[11]*U[7] - 1.38291034440645*U[12]*U[3] + 0.612715105162524*U[12]*U[6] + 1.79985224341047*U[13]*U[2] - 0.0412871146288803*U[14] + 0.0173705927405276*U[15] - 1.87294455066922*U[16]*U[2] + 1.87294455066922*U[17]*U[1] + 0.0124282982791587*U[17] + 0.00452503199094866*U[24] + 0.015412683237731*U[4] - 0.0124282982791587*U[7]\n", + "\tF[15] = 1.16886956037576*U[0]*U[14] - 0.422511733532696*U[10]*U[4] + 1.38291034440645*U[11]*U[3] + 0.612715105162524*U[11]*U[6] + 0.612715105162524*U[12]*U[7] - 1.79985224341047*U[13]*U[1] - 0.0159138129390846*U[13] - 0.0173705927405276*U[14] - 0.0412871146288803*U[15] - 1.87294455066922*U[16]*U[1] - 0.0124282982791587*U[16] - 1.87294455066922*U[17]*U[2] + 0.00452503199094866*U[25] + 0.0159138129390846*U[3] + 0.015412683237731*U[5] + 0.0124282982791587*U[6]\n", + "\tF[16] = -2.94535914360826*U[0]*U[17] + 0.569389237785845*U[10]*U[7] - 0.768581081081081*U[11]*U[5] - 0.768581081081081*U[12]*U[4] + 0.911022780457352*U[13]*U[9] + 1.42736486486486*U[14]*U[2] + 1.42736486486486*U[15]*U[1] + 0.0109797297297297*U[15] - 0.0460906434981493*U[16] + 0.0306919594705944*U[17] - 4.71257462977322*U[19]*U[3] + 0.00399762116767931*U[26] - 0.0109797297297297*U[5] + 0.0185698198198198*U[6]\n", + "\tF[17] = 2.94535914360826*U[0]*U[16] - 0.569389237785845*U[10]*U[6] + 0.768581081081081*U[11]*U[4] - 0.768581081081081*U[12]*U[5] - 0.911022780457352*U[13]*U[8] - 1.42736486486486*U[14]*U[1] - 0.0109797297297297*U[14] + 1.42736486486486*U[15]*U[2] - 0.0306919594705944*U[16] - 0.0460906434981493*U[17] + 4.71257462977322*U[18]*U[3] + 0.00399762116767931*U[27] + 0.0109797297297297*U[4] + 0.0185698198198198*U[7]\n", + "\tF[18] = -2.37660377951895*U[0]*U[19] + 0.0288656329496225*U[10]*U[9] + 1.50101291338039*U[13]*U[7] - 3.30992591155675*U[17]*U[3] - 0.0513521112007289*U[18] + 0.026256705211838*U[19] + 0.00341993024749443*U[28] + 0.0220279383429672*U[8]\n", + "\tF[19] = 2.37660377951895*U[0]*U[18] - 0.0288656329496225*U[10]*U[8] - 1.50101291338039*U[13]*U[6] + 3.30992591155675*U[16]*U[3] - 0.026256705211838*U[18] - 0.0513521112007289*U[19] + 0.00341993024749443*U[29] + 0.0220279383429672*U[9]\n", + "\tF[20] = 6.44421938347898e-6*C_go + 0.0172043158333333*U[10] - 0.00976477713178294*U[20]\n", + "\tF[21] = 0.0172043158333333*U[11] - 0.00976477713178294*U[21]\n", + "\tF[22] = 0.0172043158333333*U[12] - 0.00976477713178294*U[22]\n", + "\tF[23] = 0.0172043158333333*U[13] - 0.00976477713178294*U[23]\n", + "\tF[24] = 0.0172043158333333*U[14] - 0.00976477713178294*U[24]\n", + "\tF[25] = 0.0172043158333333*U[15] - 0.00976477713178294*U[25]\n", + "\tF[26] = 0.0172043158333333*U[16] - 0.00976477713178294*U[26]\n", + "\tF[27] = 0.0172043158333333*U[17] - 0.00976477713178294*U[27]\n", + "\tF[28] = 0.0172043158333333*U[18] - 0.00976477713178294*U[28]\n", + "\tF[29] = 0.0172043158333333*U[19] - 0.00976477713178294*U[29]\n", + "\treturn F\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cededb7", + "metadata": {}, + "outputs": [], + "source": [ + "offset = 1 if model_parameters.dynamic_T else 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7e03156", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.ndim" + ] + }, + { + "cell_type": "markdown", + "id": "0579478b", + "metadata": {}, + "source": [ + "## Comparing with numerical results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77155759", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f38b345f", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0f5941", + "metadata": {}, + "outputs": [], + "source": [ + "integrator = RungeKuttaIntegrator()\n", + "integrator.set_func(f)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "415ab585", + "metadata": {}, + "outputs": [], + "source": [ + "integrator_num = RungeKuttaIntegrator()\n", + "integrator_num.set_func(f_num)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2b5f14a", + "metadata": {}, + "outputs": [], + "source": [ + "# ICs calculated from long transient\n", + "\n", + "ic = np.array([0.05055959, -0.01639403, -0.01440781, -0.01846523, -0.01352099,\n", + " 0.011685 , -0.00201673, -0.02030682, 0.03923588, -0.02229535,\n", + " 0.0586372 , -0.01805569, -0.01264252, -0.0103574 , -0.00618456,\n", + " 0.01159318, -0.00478694, -0.00782509, 0.01066059, -0.01552667,\n", + " 0.30718325, -0.03247899, -0.04512935, -0.00078786, -0.00067468,\n", + " 0.00183836, 0.00068025, 0.00215424, -0.00322845, -0.00186392])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c6d5773", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", + "reference_time, reference_traj = integrator.get_trajectories()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b38989ba", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator_num.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", + "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f892676", + "metadata": {}, + "outputs": [], + "source": [ + "varx = 1\n", + "vary = 20\n", + "plt.figure(figsize=(12, 10))\n", + "\n", + "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", + "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", + "\n", + "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68781088", + "metadata": {}, + "outputs": [], + "source": [ + "varx = 2\n", + "vary = 1\n", + "plt.figure(figsize=(12, 10))\n", + "\n", + "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", + "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", + "\n", + "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6697d107", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 8))\n", + "t_s = 10000\n", + "\n", + "plt.plot(reference_time_num[:t_s] * model_parameters.dimensional_time, reference_traj_num[varx, :t_s])\n", + "plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76ecb077", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index e60ba8d..9fef77c 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -97,47 +97,6 @@ "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "6112c83a", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "# Takes ~5 mins\n", - "ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True) # <- Can be turned off once saved\n", - "ocn_ip = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35d77653", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "# Takes ~5 mins\n", - "atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True) # <- Can be turned off once saved\n", - "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "70086b90", - "metadata": {}, - "outputs": [], - "source": [ - "atm_ip_stored.save_to_file('temp_atm_sym_lin.ip') # <- Can be turned off once saved\n", - "ocn_ip_stored.save_to_file('temp_ocn_sym_lin.ip')" - ] - }, { "cell_type": "code", "execution_count": null, @@ -187,7 +146,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, print_equations, equation_as_function" + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations" ] }, { @@ -208,77 +167,37 @@ "outputs": [], "source": [ "%%time\n", - "funcs, = create_symbolic_equations(model_parameters, atm_loaded, ocn_loaded)" - ] - }, - { - "cell_type": "markdown", - "id": "0c185e14", - "metadata": {}, - "source": [ - "Convert the dictionary of functions to a lambdified function (not working with numba)\n", - "The input `remain_variables` specifies which variables not to substitute with values." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "870fe100", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "fx = equation_as_function(funcs, model_parameters, remain_variables={'eps', 'n'}, language='python', string_output=False)" + "funcs, = create_symbolic_equations(model_parameters, continuation_variables={'eps'})" ] }, { "cell_type": "code", "execution_count": null, - "id": "3ec572eb", - "metadata": {}, + "id": "5fd8283d", + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "from numba import njit" + "print(funcs)" ] }, { "cell_type": "markdown", - "id": "0340486e", + "id": "2bd791a9", "metadata": {}, "source": [ - "Produce the tendencies as a string and output this string defining the below function" + "Below function has the **kwargs input removed as I cannot get this to work with numba" ] }, { "cell_type": "code", "execution_count": null, - "id": "38012b9c", + "id": "0632dd0f", "metadata": {}, "outputs": [], "source": [ - "%%time\n", - "function_text = equation_as_function(funcs, model_parameters, remain_variables={'eps', 'n'}, language='python', string_output=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c385c4be", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "for i in function_text:\n", - " print(i)" - ] - }, - { - "cell_type": "markdown", - "id": "2bd791a9", - "metadata": {}, - "source": [ - "Below function has the **kwargs input removed as I cannot get this to work with numba" + "from numba import njit" ] }, { @@ -333,6 +252,26 @@ "\treturn U_out" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a2d58c7", + "metadata": {}, + "outputs": [], + "source": [ + "offset = 1 if model_parameters.dynamic_T else 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44be2ce1", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.ndim" + ] + }, { "cell_type": "markdown", "id": "0579478b", @@ -456,59 +395,11 @@ "source": [ "plt.figure(figsize=(12, 8))\n", "\n", - "plt.plot(reference_traj_num[varx, :] - reference_traj[varx, :])\n", - "plt.plot(reference_traj_num[vary, :] - reference_traj[vary, :])\n", + "plt.plot(reference_traj_num[varx, :])\n", + "plt.plot(reference_traj[varx, :])\n", "plt.show()" ] }, - { - "cell_type": "markdown", - "id": "1417d44f", - "metadata": {}, - "source": [ - "### Testing lambda function" - ] - }, - { - "cell_type": "markdown", - "id": "ea6f7466", - "metadata": {}, - "source": [ - "I dont know enough about numba to work out how to make the lambified version work here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e49d480b", - "metadata": {}, - "outputs": [], - "source": [ - "fx(0, ic, epsilon=0.76, n=1.3)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "91fda04c", - "metadata": {}, - "outputs": [], - "source": [ - "@njit\n", - "def f_test(t, U):\n", - " return fx(t, U, n=1.5, epsilon=0.76)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b1eeb348", - "metadata": {}, - "outputs": [], - "source": [ - "f_test(0, ic)" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index a9e47de..5533bbc 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -118,85 +118,6 @@ "model_parameters.print_params()" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "6112c83a", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "# Takes ~5 mins\n", - "# atm_ip = AtmosphericSymbolicInnerProducts(model_parameters)\n", - "# ocn_ip_stored = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)\n", - "ocn_ip = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35d77653", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "# Takes ~5 mins\n", - "# atm_ip_stored = AtmosphericSymbolicInnerProducts(model_parameters, return_symbolic=True)\n", - "atm_ip = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "70086b90", - "metadata": {}, - "outputs": [], - "source": [ - "# atm_ip_stored.save_to_file('temp_atm_sym_de.ip')\n", - "# ocn_ip_stored.save_to_file('temp_ocn_sym_de.ip')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea85360f", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)\n", - "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "196d5440", - "metadata": {}, - "outputs": [], - "source": [ - "atm_loaded.load_from_file('temp_atm_sym_de.ip')\n", - "ocn_loaded.load_from_file('temp_ocn_sym_de.ip')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f1ffbb0", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "ocn_num = OceanicSymbolicInnerProducts(model_parameters, stored=True)\n", - "atm_num = AtmosphericSymbolicInnerProducts(model_parameters, stored=True)" - ] - }, { "cell_type": "markdown", "id": "d43d341b", @@ -212,7 +133,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, print_equations, equation_as_function" + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function" ] }, { @@ -224,7 +145,7 @@ }, "outputs": [], "source": [ - "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True, )" + "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" ] }, { @@ -237,103 +158,92 @@ "outputs": [], "source": [ "%%time\n", - "funcs = create_symbolic_equations(model_parameters, atm_loaded, ocn_loaded)" + "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables={}, return_symbolic_qgtensor=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "c3aaf1ae", - "metadata": { - "scrolled": true - }, + "id": "ecd9ee51", + "metadata": {}, "outputs": [], "source": [ - "ten = SymbolicTensorDynamicT(model_parameters, atm_loaded, ocn_loaded, numerically_test_tensor=False)" + "print(funcs)" ] }, { "cell_type": "code", "execution_count": null, - "id": "e486fb7c", - "metadata": { - "scrolled": true - }, + "id": "64fc47ac", + "metadata": {}, "outputs": [], "source": [ - "ten.test_tensor_numerically(tol=1e-14)" + "import sparse as sp" ] }, { "cell_type": "code", "execution_count": null, - "id": "6ac0c25c", + "id": "f97405e2", "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(ten.tensor_dic)" + "subbed_tensor = ten.sub_tensor()\n", + "dims = (model_parameters.ndim + 1, model_parameters.ndim + 1, model_parameters.ndim + 1, model_parameters.ndim + 1, model_parameters.ndim + 1)\n", + "coords = np.array([list(k) for k in subbed_tensor.keys()]).T\n", + "data = np.array(list(subbed_tensor.values()), dtype=float)\n", + "subbed_tensor_sp = sp.COO(coords, data, shape=dims)" ] }, { "cell_type": "code", "execution_count": null, - "id": "79c77094", + "id": "6d842e3d", "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(ten_num.tensor.todense())" - ] - }, - { - "cell_type": "markdown", - "id": "3584b521", - "metadata": {}, - "source": [ - "I the error is comming from the _O and _C terms having some non zeros that should be zero" + "num_t = ten_num.tensor.todense()" ] }, { "cell_type": "code", "execution_count": null, - "id": "1a4a2519", + "id": "4c62f70f", "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(ocn_loaded._O)" + "err = subbed_tensor_sp.todense() - num_t" ] }, { "cell_type": "code", "execution_count": null, - "id": "aa2d7a41", + "id": "14fc36c9", "metadata": { "scrolled": true }, "outputs": [], "source": [ - "ten.print_tensor(ocn_num._O.todense())" + "for i in range(model_parameters.ndim + 1):\n", + " for j in range(model_parameters.ndim + 1):\n", + " for k in range(model_parameters.ndim + 1):\n", + " for ell in range(model_parameters.ndim + 1):\n", + " for m in range(model_parameters.ndim + 1):\n", + " if abs(err[i, j, k, ell, m]) > 1e-15:\n", + " if num_t[i, j, k, ell, m] != 0:\n", + " print(i, j, k, ell, m, err[i, j, k, ell, m] / num_t[i, j, k, ell, m])\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "12a879bc", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "fx = equation_as_function(funcs[0], model_parameters, variables=True, language='python', string_output=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bccb3e42", - "metadata": {}, + "id": "5970236e", + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ - "for i in fx:\n", - " print(i)" + "ten.print_tensor(num_t)" ] }, { @@ -356,46 +266,47 @@ "@njit\n", "def f(t, U):\n", "\t#Tendency function of the qgs model\n", - "\tU_out = np.empty_like(U)\n", - "\tU_out[0] = -0.0145*U[0] + 0.0145*U[11] + 0.0505574149661188*U[22] + 0.0734121368001177*U[24]\n", - "\tU_out[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[11]*U[13] + 0.0145*U[12] - 1.9945469157942*U[14]*U[16] - 2.59615384615385*U[15]*U[18] + 2.59615384615385*U[16]*U[17] - 0.0145*U[1] - 0.00295864958311857*U[21] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", - "\tU_out[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[11]*U[12] + 0.0145*U[13] + 1.9945469157942*U[14]*U[15] + 2.59615384615385*U[15]*U[17] + 2.59615384615385*U[16]*U[18] - 0.115315741885204*U[1] + 0.0145*U[25] - 0.0145*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", - "\tU_out[3] = 2.16075915877705*U[12]*U[16] - 2.16075915877705*U[13]*U[15] + 0.0145*U[14] + 4.32151831755411*U[17]*U[20] - 4.32151831755411*U[18]*U[19] + 2.16075915877705*U[1]*U[5] - 0.00216427290094687*U[21] + 0.0238416302768307*U[23] - 2.16075915877705*U[2]*U[4] - 0.0145*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", - "\tU_out[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[11]*U[16] + 2.43*U[12]*U[18] + 0.345721465404329*U[13]*U[14] - 2.43*U[13]*U[17] + 0.0145*U[15] + 2.43*U[1]*U[7] - 0.00449241352700723*U[22] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.0145*U[4] + 0.0599641857803059*U[5]\n", - "\tU_out[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[11]*U[15] - 0.345721465404329*U[12]*U[14] - 2.43*U[12]*U[17] - 2.43*U[13]*U[18] + 0.0145*U[16] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.0145*U[26] - 2.43*U[2]*U[7] - 0.0599641857803059*U[4] - 0.0145*U[5]\n", - "\tU_out[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[11]*U[18] + 0.675*U[12]*U[16] + 0.675*U[13]*U[15] - 5.18582198106493*U[14]*U[20] + 0.0145*U[17] + 0.675*U[1]*U[5] - 0.000192312222902707*U[21] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.0145*U[6] + 0.0749552322253824*U[7]\n", - "\tU_out[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[11]*U[17] - 0.675*U[12]*U[15] + 0.675*U[13]*U[16] + 5.18582198106493*U[14]*U[19] + 0.0145*U[18] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.0145*U[7]\n", - "\tU_out[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[11]*U[20] - 2.65939588772561*U[14]*U[18] + 0.0145*U[19] - 0.000431962839135311*U[22] - 2.65939588772561*U[3]*U[7] - 0.0145*U[8] + 0.0576578709426019*U[9]\n", - "\tU_out[9] = 2.65939588772561*U[0]*U[8] + 2.65939588772561*U[11]*U[19] + 2.65939588772561*U[14]*U[17] + 0.0145*U[20] + 2.65939588772561*U[3]*U[6] - 0.0576578709426019*U[8] - 0.0145*U[9]\n", - "\tU_out[10] = -0.000510753549879668*U[10]**4 - 0.014593023255814*U[10] - 0.00183644645351737*U[21] - 0.00374635076517544*U[23] + 1.59610484337396e-5*U[29]**4 + 5.17501542233338e-5*U[29]**3*U[30] + 1.72500514077779e-5*U[29]**3*U[32] + 0.00729651162790698*U[29] + 0.00591432955679692*U[30] + 0.00197144318559898*U[32] + 0.000531003677198668\n", - "\tU_out[11] = 0.00131818181818182*U[0] - 0.00185728563592607*U[10]**3*U[11] - 0.0198572938689218*U[11] - 1.63693875664928*U[12]*U[2] + 1.63693875664928*U[13]*U[1] - 1.30955100531943*U[15]*U[5] + 1.30955100531943*U[16]*U[4] - 3.27387751329857*U[17]*U[7] + 3.27387751329857*U[18]*U[6] - 2.61910201063885*U[19]*U[9] + 2.61910201063885*U[20]*U[8] - 0.00459612863328353*U[22] - 0.00667383061819252*U[24] + 4.43550120954776e-5*U[29]**3*U[31] + 1.7742004838191e-5*U[29]**3*U[33] + 0.00506916671004016*U[31] + 0.00202766668401607*U[33] + 0.000482730615635153\n", - "\tU_out[12] = -1.6647358298754*U[0]*U[13] - 0.0015418975090707*U[10]**3*U[12] + 1.05320021890077*U[11]*U[2] - 0.0287966213251426*U[12] + 0.0282849932925971*U[13] + 1.68512035024123*U[14]*U[5] + 1.06132075471698*U[15]*U[7] - 2.66357732780065*U[16]*U[3] - 1.06132075471698*U[16]*U[6] + 2.33490566037736*U[17]*U[5] - 2.33490566037736*U[18]*U[4] + 0.00355660377358491*U[1] + 0.000725706501519649*U[21] - 2.04500508591402e-5*U[29]**3*U[30] - 0.00233715902975374*U[30]\n", - "\tU_out[13] = 1.6647358298754*U[0]*U[12] - 0.0015418975090707*U[10]**3*U[13] - 1.05320021890077*U[11]*U[1] - 0.0282849932925971*U[12] - 0.0287966213251426*U[13] - 1.68512035024123*U[14]*U[4] + 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] - 1.06132075471698*U[16]*U[7] + 2.33490566037736*U[17]*U[4] + 2.33490566037736*U[18]*U[5] - 0.00355660377358491*U[25] + 4.81842971584593e-5*U[29]**3*U[34] + 0.00355660377358491*U[2] + 0.00550680122860904*U[34]\n", - "\tU_out[14] = -0.00145929585679905*U[10]**3*U[14] - 1.44050610585137*U[12]*U[5] + 1.44050610585137*U[13]*U[4] - 0.0311378737541528*U[14] - 2.67522562515254*U[15]*U[2] + 2.67522562515254*U[16]*U[1] - 2.88101221170274*U[17]*U[9] + 2.88101221170274*U[18]*U[8] - 5.35045125030509*U[19]*U[7] + 5.35045125030509*U[20]*U[6] + 0.000618363685984819*U[21] - 0.00681189436480877*U[23] - 1.74251833232233e-5*U[29]**3*U[30] + 3.1365329981802e-5*U[29]**3*U[32] - 0.00199145835037292*U[30] + 0.00358462503067126*U[32] + 0.00414285714285714*U[3]\n", - "\tU_out[15] = -1.35185957626052*U[0]*U[16] - 0.00125723950739611*U[10]**3*U[15] + 0.421071015556554*U[11]*U[5] - 0.45*U[12]*U[7] - 1.63996079743079*U[13]*U[3] + 0.45*U[13]*U[6] + 1.90590038620335*U[14]*U[2] - 0.0368649373881932*U[15] + 0.0230631483770407*U[16] - 2.31923076923077*U[17]*U[2] + 2.31923076923077*U[18]*U[1] + 0.00172785135654124*U[22] - 1.66746568543758e-5*U[29]**3*U[31] - 0.00190568351656843*U[31] + 0.00557692307692308*U[4]\n", - "\tU_out[16] = 1.35185957626052*U[0]*U[15] - 0.00125723950739611*U[10]**3*U[16] - 0.421071015556554*U[11]*U[4] + 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 0.45*U[13]*U[7] - 1.90590038620335*U[14]*U[1] - 0.0230631483770407*U[15] - 0.0368649373881932*U[16] - 2.31923076923077*U[17]*U[1] - 2.31923076923077*U[18]*U[2] - 0.00557692307692308*U[26] + 3.92887346061283e-5*U[29]**3*U[35] + 0.00449016100178891*U[35] + 0.00557692307692308*U[5]\n", - "\tU_out[17] = -3.421202001397*U[0]*U[18] - 0.00102150709975934*U[10]**3*U[17] + 0.180063263231421*U[11]*U[7] - 0.7875*U[12]*U[5] - 0.7875*U[13]*U[4] + 0.288101221170274*U[14]*U[9] + 1.4625*U[15]*U[2] + 1.4625*U[16]*U[1] - 0.043546511627907*U[17] + 0.0374776161126912*U[18] - 5.47392320223521*U[20]*U[3] + 9.61561114513534e-5*U[21] - 2.70963173883607e-6*U[29]**3*U[30] - 0.00030967357144237*U[30] + 0.00725*U[6]\n", - "\tU_out[18] = 3.421202001397*U[0]*U[17] - 0.00102150709975934*U[10]**3*U[18] - 0.180063263231421*U[11]*U[6] + 0.7875*U[12]*U[4] - 0.7875*U[13]*U[5] - 0.288101221170274*U[14]*U[8] - 1.4625*U[15]*U[1] + 1.4625*U[16]*U[2] - 0.0374776161126912*U[17] - 0.043546511627907*U[18] + 5.47392320223521*U[19]*U[3] + 0.00725*U[7]\n", - "\tU_out[19] = -2.75575081119393*U[0]*U[20] - 0.000888267043268988*U[10]**3*U[19] - 0.25052280101763*U[11]*U[9] + 1.00209120407052*U[14]*U[7] - 4.00836481628207*U[18]*U[3] - 0.0473230535894843*U[19] + 0.0325892314023402*U[20] + 0.00024415290907648*U[22] - 2.35620151203137e-6*U[29]**3*U[31] - 0.000269281366471626*U[31] + 0.00819565217391304*U[8]\n", - "\tU_out[20] = 2.75575081119393*U[0]*U[19] - 0.000888267043268988*U[10]**3*U[20] + 0.25052280101763*U[11]*U[8] - 1.00209120407052*U[14]*U[6] + 4.00836481628207*U[17]*U[3] - 0.0325892314023402*U[19] - 0.0473230535894843*U[20] + 0.00819565217391304*U[9]\n", - "\tU_out[21] = 2.30554431681946e-7*U[12] + 2.5547312812995e-7*U[14] + 1.41879650265813e-7*U[17] - 2.30554431681946e-7*U[1] - 4.98594961275655e-7*U[21] - 0.000231547500016414*U[22]*U[25] - 0.000393263214313592*U[22]*U[27] - 0.000779175714340949*U[23]*U[26] - 0.00102174928578672*U[23]*U[28] - 0.00156202678582502*U[24]*U[27] - 2.49430228227486e-5*U[25] - 2.5547312812995e-7*U[3] - 1.41879650265813e-7*U[6]\n", - "\tU_out[22] = 1.27676499203106e-7*U[0] - 1.27676499203106e-7*U[11] + 4.43165421807241e-7*U[15] + 1.84356815471812e-7*U[19] - 0.000297563940911795*U[21]*U[25] + 0.00056941247952257*U[21]*U[27] - 1.45521268835226e-6*U[22] - 0.00185518259457354*U[23]*U[25] - 0.00363689261114416*U[24]*U[26] - 2.49312940023328e-5*U[26] - 4.43165421807241e-7*U[4] - 1.84356815471812e-7*U[8]\n", - "\tU_out[23] = -4.59275459509595e-7*U[14] - 0.00110122563125871*U[21]*U[26] + 0.0019601816236405*U[21]*U[28] + 0.000385428970940548*U[22]*U[25] - 3.04757790976247e-6*U[23] - 0.00547309138735578*U[24]*U[25] - 2.49117704610167e-5*U[27] + 4.59275459509595e-7*U[3]\n", - "\tU_out[24] = 5.09747214298137e-8*U[0] - 5.09747214298137e-8*U[11] - 0.0028417174170155*U[21]*U[27] - 0.000594010608460015*U[22]*U[26] + 0.00259237963074834*U[23]*U[25] - 5.2727039845599e-6*U[24] - 2.48844888171634e-5*U[28]\n", - "\tU_out[25] = -5.43087366736076e-7*U[13] + 0.000529111412123567*U[21]*U[22] + 2.49364240037345e-5*U[21] + 0.00146975392256546*U[22]*U[23] + 0.00288071768822831*U[23]*U[24] - 1.03680315467796e-6*U[25] + 5.43087366736076e-7*U[2]\n", - "\tU_out[26] = -1.04390781015316e-6*U[16] + 0.00188040062849988*U[21]*U[23] + 0.00423090141412473*U[22]*U[24] + 2.49247013868922e-5*U[22] - 1.99291491029239e-6*U[26] + 1.04390781015316e-6*U[5]\n", - "\tU_out[27] = -0.000176149545706673*U[21]*U[22] + 0.00440373864266683*U[21]*U[24] + 2.49051881654425e-5*U[23] - 3.58443843007766e-6*U[27]\n", - "\tU_out[28] = -0.000938435679208948*U[21]*U[23] + 2.4877920928742e-5*U[24] - 5.80838943890229e-6*U[28]\n", - "\tU_out[29] = 9.12059910499408e-6*U[10]**4 + 4.64878611115251e-5*U[10]**3*U[12] + 1.67415119447806e-5*U[10]**3*U[14] + 9.29757222230502e-6*U[10]**3*U[17] + 0.000521179402000664*U[10] + 0.000664115245473612*U[12] + 0.000239165516521407*U[14] + 0.000132823049094722*U[17] + 1.50119582264922*U[21]*U[35] - 0.750597911324609*U[21]*U[37] - 1.50119582263017*U[22]*U[34] + 1.12589686698056*U[22]*U[36] - 4.50358746792226*U[23]*U[35] + 2.25179373396113*U[23]*U[37] - 2.62709268963613*U[24]*U[34] - 5.62948433490282*U[24]*U[36] + 1.50119582263017*U[25]*U[31] + 2.62709268963613*U[25]*U[33] - 1.50119582264922*U[26]*U[30] + 4.50358746792226*U[26]*U[32] - 1.12589686698056*U[27]*U[31] + 5.62948433490282*U[27]*U[33] + 0.750597911324609*U[28]*U[30] - 2.25179373396113*U[28]*U[32] - 8.14339205803043e-7*U[29]**4 - 1.05879118406788e-22*U[29]**3*U[30] + 4.2351647362715e-22*U[29]**3*U[32] - 0.000260589700996678*U[29] + 5.70773716830428e-5\n", - "\tU_out[30] = 3.3881317890172e-21*U[10]**4 - 5.31652508387687e-5*U[10]**3*U[12] - 2.75103051082387e-5*U[10]**3*U[14] - 1.06330501677537e-5*U[10]**3*U[17] - 1.06685493772574e-16*U[10] - 0.000759506950146691*U[12] - 0.000393006100803128*U[14] - 0.000151901390029338*U[17] - 1.21682350103801*U[21]*U[35] + 0.608411750519007*U[21]*U[37] + 2.34182350103837*U[22]*U[34] - 1.28761762577863*U[22]*U[36] + 5.15047050311451*U[23]*U[35] - 2.57523525155726*U[23]*U[37] + 2.12944112681652*U[24]*U[34] + 6.43808812889314*U[24]*U[36] - 2.34182350103837*U[25]*U[31] - 2.12944112681652*U[25]*U[33] + 1.21682350103801*U[26]*U[30] - 5.15047050311451*U[26]*U[32] + 1.28761762577863*U[27]*U[31] - 6.43808812889314*U[27]*U[33] - 0.608411750519007*U[28]*U[30] + 2.57523525155726*U[28]*U[32] + 1.32348898008484e-22*U[29]**4 - 3.25735682321217e-6*U[29]**3*U[30] - 8.470329472543e-22*U[29]**3*U[32] - 0.000260589700996678*U[30] - 1.16551733542192e-17\n", - "\tU_out[31] = 2.78802933171573e-5*U[10]**3*U[11] - 1.54836099362061e-5*U[10]**3*U[15] - 3.09672198724122e-6*U[10]**3*U[19] + 0.000398291670074584*U[11] - 0.000221195408173121*U[15] - 4.42390816346243e-5*U[19] - 1.125*U[21]*U[34] + 0.375*U[21]*U[36] + 1.875*U[23]*U[34] + 2.25*U[24]*U[35] + 1.125*U[25]*U[30] - 1.875*U[25]*U[32] - 2.25*U[26]*U[33] - 0.375*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[31] - 0.000260589700996678*U[31] + 4.36192251724445e-5\n", - "\tU_out[32] = -8.470329472543e-22*U[10]**4 - 1.25605469675209e-5*U[10]**3*U[12] + 2.05688778355549e-5*U[10]**3*U[14] - 2.51210939350418e-6*U[10]**3*U[17] - 5.42101086242752e-20*U[10] - 0.000179437180657833*U[12] + 0.000293842414478535*U[14] - 3.58874361315667e-5*U[17] - 1.90560783367934*U[21]*U[35] + 0.952803916839669*U[21]*U[37] - 1.46939216632066*U[22]*U[34] - 0.304205875259503*U[22]*U[36] + 1.21682350103801*U[23]*U[35] - 0.608411750519007*U[23]*U[37] + 3.33481370893884*U[24]*U[34] + 1.52102937629752*U[24]*U[36] + 1.46939216632066*U[25]*U[31] - 3.33481370893884*U[25]*U[33] + 1.90560783367934*U[26]*U[30] - 1.21682350103801*U[26]*U[32] + 0.304205875259503*U[27]*U[31] - 1.52102937629752*U[27]*U[33] - 0.952803916839669*U[28]*U[30] + 0.608411750519007*U[28]*U[32] + 1.05879118406788e-22*U[29]**4 - 4.2351647362715e-22*U[29]**3*U[30] - 3.25735682321217e-6*U[29]**3*U[32] - 0.000260589700996678*U[32]\n", - "\tU_out[33] = 1.11521173268629e-5*U[10]**3*U[11] + 0.000159316668029834*U[11] - 1.875*U[21]*U[36] - 2.25*U[22]*U[35] - 2.625*U[23]*U[34] + 2.625*U[25]*U[32] + 2.25*U[26]*U[31] + 1.875*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[33] - 0.000260589700996678*U[33] + 1.74476900689778e-5\n", - "\tU_out[34] = 3.64823964199763e-5*U[10]**3*U[13] + 0.000521179401993355*U[13] + 1.125*U[21]*U[31] - 1.125*U[22]*U[30] + 1.875*U[22]*U[32] - 1.875*U[23]*U[31] + 2.625*U[23]*U[33] - 2.625*U[24]*U[32] - 3.25735682321217e-6*U[29]**3*U[34] - 0.000260589700996678*U[34]\n", - "\tU_out[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", - "\tU_out[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", - "\tU_out[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", - "\treturn U_out\n" + "\tF = np.empty_like(U)\n", + "\tF[0] = -0.0145*U[0] + 0.0145*U[11] + 0.0505574149661188*U[22] + 0.0734121368001177*U[24]\n", + "\tF[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[11]*U[13] + 0.0145*U[12] - 1.9945469157942*U[14]*U[16] - 2.59615384615385*U[15]*U[18] + 2.59615384615385*U[16]*U[17] - 0.0145*U[1] - 0.00295864958311857*U[21] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", + "\tF[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[11]*U[12] + 0.0145*U[13] + 1.9945469157942*U[14]*U[15] + 2.59615384615385*U[15]*U[17] + 2.59615384615385*U[16]*U[18] - 0.115315741885204*U[1] + 0.0145*U[25] - 0.0145*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", + "\tF[3] = 2.16075915877706*U[12]*U[16] - 2.16075915877706*U[13]*U[15] + 0.0145*U[14] + 4.32151831755411*U[17]*U[20] - 4.32151831755411*U[18]*U[19] + 2.16075915877706*U[1]*U[5] - 0.00216427290094687*U[21] + 0.0238416302768307*U[23] - 2.16075915877706*U[2]*U[4] - 0.0145*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", + "\tF[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[11]*U[16] + 2.43*U[12]*U[18] + 0.345721465404329*U[13]*U[14] - 2.43*U[13]*U[17] + 0.0145*U[15] + 2.43*U[1]*U[7] - 0.00449241352700723*U[22] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.0145*U[4] + 0.059964185780306*U[5]\n", + "\tF[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[11]*U[15] - 0.345721465404329*U[12]*U[14] - 2.43*U[12]*U[17] - 2.43*U[13]*U[18] + 0.0145*U[16] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.0145*U[26] - 2.43*U[2]*U[7] - 0.059964185780306*U[4] - 0.0145*U[5]\n", + "\tF[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[11]*U[18] + 0.675*U[12]*U[16] + 0.675*U[13]*U[15] - 5.18582198106493*U[14]*U[20] + 0.0145*U[17] + 0.675*U[1]*U[5] - 0.000192312222902707*U[21] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.0145*U[6] + 0.0749552322253824*U[7]\n", + "\tF[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[11]*U[17] - 0.675*U[12]*U[15] + 0.675*U[13]*U[16] + 5.18582198106493*U[14]*U[19] + 0.0145*U[18] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.0145*U[7]\n", + "\tF[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[11]*U[20] - 2.65939588772561*U[14]*U[18] + 0.0145*U[19] - 0.000431962839135312*U[22] - 2.65939588772561*U[3]*U[7] - 0.0145*U[8] + 0.0576578709426019*U[9]\n", + "\tF[9] = 2.6593958877256*U[0]*U[8] + 2.6593958877256*U[11]*U[19] + 2.6593958877256*U[14]*U[17] + 0.0145*U[20] + 2.6593958877256*U[3]*U[6] - 0.0576578709426019*U[8] - 0.0145*U[9]\n", + "\tF[10] = -0.000510753549879668*U[10]**4 - 0.014593023255814*U[10] - 0.00183644645351737*U[21] - 0.00374635076517544*U[23] + 1.59610484337396e-5*U[29]**4 + 5.17501542233338e-5*U[29]**3*U[30] + 1.72500514077779e-5*U[29]**3*U[32] + 0.00729651162790698*U[29] + 0.00591432955679693*U[30] + 0.00197144318559898*U[32] + 0.000531003677198668\n", + "\tF[11] = 0.00131818181818182*U[0] - 0.00185728563592607*U[10]**3*U[11] - 0.0198572938689218*U[11] - 1.63693875664928*U[12]*U[2] + 1.63693875664928*U[13]*U[1] - 1.30955100531943*U[15]*U[5] + 1.30955100531943*U[16]*U[4] - 3.27387751329857*U[17]*U[7] + 3.27387751329857*U[18]*U[6] - 2.61910201063885*U[19]*U[9] + 2.61910201063885*U[20]*U[8] - 0.00459612863328353*U[22] - 0.00667383061819252*U[24] + 4.43550120954776e-5*U[29]**3*U[31] + 1.7742004838191e-5*U[29]**3*U[33] + 0.00506916671004016*U[31] + 0.00202766668401606*U[33] + 0.000482730615635153\n", + "\tF[12] = -1.6647358298754*U[0]*U[13] - 0.0015418975090707*U[10]**3*U[12] + 1.05320021890077*U[11]*U[2] - 0.0287966213251426*U[12] + 0.0282849932925971*U[13] + 1.68512035024122*U[14]*U[5] + 1.06132075471698*U[15]*U[7] - 2.66357732780065*U[16]*U[3] - 1.06132075471698*U[16]*U[6] + 2.33490566037736*U[17]*U[5] - 2.33490566037736*U[18]*U[4] + 0.0035566037735849*U[1] + 0.000725706501519649*U[21] - 2.04500508591402e-5*U[29]**3*U[30] - 0.00233715902975374*U[30]\n", + "\tF[13] = 1.6647358298754*U[0]*U[12] - 0.0015418975090707*U[10]**3*U[13] - 1.05320021890077*U[11]*U[1] - 0.0282849932925971*U[12] - 0.0287966213251426*U[13] - 1.68512035024122*U[14]*U[4] + 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] - 1.06132075471698*U[16]*U[7] + 2.33490566037736*U[17]*U[4] + 2.33490566037736*U[18]*U[5] - 0.0035566037735849*U[25] + 4.81842971584593e-5*U[29]**3*U[34] + 0.0035566037735849*U[2] + 0.00550680122860904*U[34]\n", + "\tF[14] = -0.00145929585679905*U[10]**3*U[14] - 1.44050610585137*U[12]*U[5] + 1.44050610585137*U[13]*U[4] - 0.0311378737541528*U[14] - 2.67522562515254*U[15]*U[2] + 2.67522562515254*U[16]*U[1] - 2.88101221170274*U[17]*U[9] + 2.88101221170274*U[18]*U[8] - 5.35045125030509*U[19]*U[7] + 5.35045125030509*U[20]*U[6] + 0.000618363685984819*U[21] - 0.00681189436480877*U[23] - 1.74251833232233e-5*U[29]**3*U[30] + 3.1365329981802e-5*U[29]**3*U[32] - 0.00199145835037292*U[30] + 0.00358462503067126*U[32] + 0.00414285714285714*U[3]\n", + "\tF[15] = -1.35185957626052*U[0]*U[16] - 0.00125723950739611*U[10]**3*U[15] + 0.421071015556554*U[11]*U[5] - 0.449999999999999*U[12]*U[7] - 1.63996079743079*U[13]*U[3] + 0.45*U[13]*U[6] + 1.90590038620335*U[14]*U[2] - 0.0368649373881932*U[15] + 0.0230631483770408*U[16] - 2.31923076923077*U[17]*U[2] + 2.31923076923077*U[18]*U[1] + 0.00172785135654124*U[22] - 1.66746568543758e-5*U[29]**3*U[31] - 0.00190568351656843*U[31] + 0.00557692307692308*U[4]\n", + "\tF[16] = 1.35185957626052*U[0]*U[15] - 0.00125723950739611*U[10]**3*U[16] - 0.421071015556554*U[11]*U[4] + 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 0.45*U[13]*U[7] - 1.90590038620335*U[14]*U[1] - 0.0230631483770408*U[15] - 0.0368649373881932*U[16] - 2.31923076923077*U[17]*U[1] - 2.31923076923077*U[18]*U[2] - 0.00557692307692308*U[26] + 3.92887346061283e-5*U[29]**3*U[35] + 0.00449016100178891*U[35] + 0.00557692307692308*U[5]\n", + "\tF[17] = -3.421202001397*U[0]*U[18] - 0.00102150709975934*U[10]**3*U[17] + 0.180063263231421*U[11]*U[7] - 0.7875*U[12]*U[5] - 0.7875*U[13]*U[4] + 0.288101221170274*U[14]*U[9] + 1.4625*U[15]*U[2] + 1.4625*U[16]*U[1] - 0.043546511627907*U[17] + 0.0374776161126912*U[18] - 5.47392320223521*U[20]*U[3] + 9.61561114513536e-5*U[21] - 2.70963173883608e-6*U[29]**3*U[30] - 0.00030967357144237*U[30] + 0.00725*U[6]\n", + "\tF[18] = 3.421202001397*U[0]*U[17] - 0.00102150709975934*U[10]**3*U[18] - 0.180063263231421*U[11]*U[6] + 0.7875*U[12]*U[4] - 0.7875*U[13]*U[5] - 0.288101221170275*U[14]*U[8] - 1.4625*U[15]*U[1] + 1.4625*U[16]*U[2] - 0.0374776161126912*U[17] - 0.043546511627907*U[18] + 5.47392320223521*U[19]*U[3] + 0.00725*U[7]\n", + "\tF[19] = -2.75575081119392*U[0]*U[20] - 0.000888267043268989*U[10]**3*U[19] - 0.25052280101763*U[11]*U[9] + 1.00209120407052*U[14]*U[7] - 4.00836481628207*U[18]*U[3] - 0.0473230535894843*U[19] + 0.0325892314023402*U[20] + 0.00024415290907648*U[22] - 2.35620151203137e-6*U[29]**3*U[31] - 0.000269281366471626*U[31] + 0.00819565217391304*U[8]\n", + "\tF[20] = 2.75575081119392*U[0]*U[19] - 0.000888267043268989*U[10]**3*U[20] + 0.250522801017629*U[11]*U[8] - 1.00209120407052*U[14]*U[6] + 4.00836481628207*U[17]*U[3] - 0.0325892314023402*U[19] - 0.0473230535894843*U[20] + 0.00819565217391304*U[9]\n", + "\tF[21] = 2.30554431681946e-7*U[12] + 2.5547312812995e-7*U[14] + 1.41879650265813e-7*U[17] - 2.30554431681946e-7*U[1] - 4.98594961275655e-7*U[21] - 0.000231547500016414*U[22]*U[25] - 0.000393263214313593*U[22]*U[27] - 0.000779175714340951*U[23]*U[26] - 0.00102174928578672*U[23]*U[28] - 0.00156202678582502*U[24]*U[27] - 2.49430228227487e-5*U[25] - 2.5547312812995e-7*U[3] - 1.41879650265813e-7*U[6]\n", + "\tF[22] = 1.27676499203106e-7*U[0] - 1.27676499203106e-7*U[11] + 4.43165421807242e-7*U[15] + 1.84356815471813e-7*U[19] - 0.000297563940911795*U[21]*U[25] + 0.00056941247952257*U[21]*U[27] - 1.45521268835226e-6*U[22] - 0.00185518259457354*U[23]*U[25] - 0.00363689261114416*U[24]*U[26] - 2.49312940023328e-5*U[26] - 4.43165421807242e-7*U[4] - 1.84356815471813e-7*U[8]\n", + "\tF[23] = -4.59275459509596e-7*U[14] - 0.00110122563125871*U[21]*U[26] + 0.0019601816236405*U[21]*U[28] + 0.000385428970940549*U[22]*U[25] - 3.04757790976247e-6*U[23] - 0.00547309138735578*U[24]*U[25] - 2.49117704610167e-5*U[27] + 4.59275459509596e-7*U[3]\n", + "\tF[24] = 5.09747214298138e-8*U[0] - 5.09747214298138e-8*U[11] - 0.0028417174170155*U[21]*U[27] - 0.000594010608460015*U[22]*U[26] + 0.00259237963074834*U[23]*U[25] - 5.2727039845599e-6*U[24] - 2.48844888171634e-5*U[28]\n", + "\tF[25] = -5.43087366736076e-7*U[13] + 0.000529111412123567*U[21]*U[22] + 2.49364240037345e-5*U[21] + 0.00146975392256546*U[22]*U[23] + 0.00288071768822831*U[23]*U[24] - 1.03680315467796e-6*U[25] + 5.43087366736076e-7*U[2]\n", + "\tF[26] = -1.04390781015316e-6*U[16] + 0.00188040062849988*U[21]*U[23] + 0.00423090141412473*U[22]*U[24] + 2.49247013868922e-5*U[22] - 1.99291491029239e-6*U[26] + 1.04390781015316e-6*U[5]\n", + "\tF[27] = -0.000176149545706673*U[21]*U[22] + 0.00440373864266684*U[21]*U[24] + 2.49051881654425e-5*U[23] - 3.58443843007766e-6*U[27]\n", + "\tF[28] = -0.000938435679208949*U[21]*U[23] + 2.4877920928742e-5*U[24] - 5.80838943890229e-6*U[28]\n", + "\tF[29] = 9.12059910499408e-6*U[10]**4 + 4.64878611115251e-5*U[10]**3*U[12] + 1.67415119447806e-5*U[10]**3*U[14] + 9.29757222230503e-6*U[10]**3*U[17] + 0.000521179401993355*U[10] + 0.000664115245477357*U[12] + 0.000239165516524778*U[14] + 0.000132823049095472*U[17] + 1.50119582264922*U[21]*U[35] - 0.750597911324608*U[21]*U[37] - 1.50119582264922*U[22]*U[34] + 1.12589686698691*U[22]*U[36] - 4.50358746794765*U[23]*U[35] + 2.25179373397383*U[23]*U[37] - 2.62709268963613*U[24]*U[34] - 5.62948433493457*U[24]*U[36] + 1.50119582264922*U[25]*U[31] + 2.62709268963613*U[25]*U[33] - 1.50119582264922*U[26]*U[30] + 4.50358746794765*U[26]*U[32] - 1.12589686698691*U[27]*U[31] + 5.62948433493457*U[27]*U[33] + 0.750597911324608*U[28]*U[30] - 2.25179373397383*U[28]*U[32] - 8.14339205803043e-7*U[29]**4 - 1.44655701782e-21*U[29]**3*U[30] - 0.000260589700996678*U[29] + 5.70773716822424e-5\n", + "\tF[30] = -5.06294956237001e-22*U[10]**4 - 5.31652508387687e-5*U[10]**3*U[12] - 2.75103051082387e-5*U[10]**3*U[14] - 1.06330501677538e-5*U[10]**3*U[17] - 2.89312686026697e-20*U[10] - 0.000759506950146621*U[12] - 0.000393006100803065*U[14] - 0.000151901390029324*U[17] - 1.21682350103801*U[21]*U[35] + 0.608411750519007*U[21]*U[37] + 2.34182350103801*U[22]*U[34] - 1.28761762577851*U[22]*U[36] + 5.15047050311404*U[23]*U[35] - 2.57523525155702*U[23]*U[37] + 2.12944112681652*U[24]*U[34] + 6.43808812889255*U[24]*U[36] - 2.34182350103801*U[25]*U[31] - 2.12944112681652*U[25]*U[33] + 1.21682350103801*U[26]*U[30] - 5.15047050311404*U[26]*U[32] + 1.28761762577851*U[27]*U[31] - 6.43808812889255*U[27]*U[33] - 0.608411750519007*U[28]*U[30] + 2.57523525155702*U[28]*U[32] + 4.52049068068751e-23*U[29]**4 - 3.25735682321217e-6*U[29]**3*U[30] - 3.61639254455001e-22*U[29]**3*U[32] - 0.000260589700996678*U[30] - 3.16843061133567e-21\n", + "\tF[31] = 2.78802933171573e-5*U[10]**3*U[11] - 1.54836099362061e-5*U[10]**3*U[15] - 3.09672198724123e-6*U[10]**3*U[19] + 0.000398291670074584*U[11] - 0.000221195408173122*U[15] - 4.42390816346244e-5*U[19] - 1.125*U[21]*U[34] + 0.375*U[21]*U[36] + 1.875*U[23]*U[34] + 2.25*U[24]*U[35] + 1.125*U[25]*U[30] - 1.875*U[25]*U[32] - 2.25*U[26]*U[33] - 0.375*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[31] - 0.000260589700996678*U[31] + 4.36192251724445e-5\n", + "\tF[32] = -5.06294956237001e-22*U[10]**4 - 1.25605469675209e-5*U[10]**3*U[12] + 2.05688778355549e-5*U[10]**3*U[14] - 2.51210939350418e-6*U[10]**3*U[17] - 2.89312686026697e-20*U[10] - 0.000179437180657833*U[12] + 0.000293842414478535*U[14] - 3.58874361315667e-5*U[17] - 1.90560783367934*U[21]*U[35] + 0.952803916839669*U[21]*U[37] - 1.46939216632066*U[22]*U[34] - 0.304205875259504*U[22]*U[36] + 1.21682350103801*U[23]*U[35] - 0.608411750519007*U[23]*U[37] + 3.33481370893884*U[24]*U[34] + 1.52102937629752*U[24]*U[36] + 1.46939216632066*U[25]*U[31] - 3.33481370893884*U[25]*U[33] + 1.90560783367934*U[26]*U[30] - 1.21682350103801*U[26]*U[32] + 0.304205875259504*U[27]*U[31] - 1.52102937629752*U[27]*U[33] - 0.952803916839669*U[28]*U[30] + 0.608411750519007*U[28]*U[32] + 4.52049068068751e-23*U[29]**4 + 3.61639254455001e-22*U[29]**3*U[30] - 3.25735682321217e-6*U[29]**3*U[32] - 0.000260589700996678*U[32] - 3.16843061133567e-21\n", + "\tF[33] = 1.11521173268629e-5*U[10]**3*U[11] + 0.000159316668029834*U[11] - 1.875*U[21]*U[36] - 2.25*U[22]*U[35] - 2.625*U[23]*U[34] + 2.625*U[25]*U[32] + 2.25*U[26]*U[31] + 1.875*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[33] - 0.000260589700996678*U[33] + 1.74476900689778e-5\n", + "\tF[34] = 3.64823964199763e-5*U[10]**3*U[13] + 0.000521179401993355*U[13] + 1.125*U[21]*U[31] - 1.125*U[22]*U[30] + 1.875*U[22]*U[32] - 1.875*U[23]*U[31] + 2.625*U[23]*U[33] - 2.625*U[24]*U[32] - 3.25735682321217e-6*U[29]**3*U[34] - 0.000260589700996678*U[34]\n", + "\tF[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", + "\tF[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", + "\tF[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", + "\treturn F\n", + "\n" ] }, { @@ -442,16 +353,16 @@ "metadata": {}, "outputs": [], "source": [ - "ic = np.array([ 2.47290678e-02, 1.06856407e-05, 6.14020503e-06, 1.33706189e-04,\n", - " -2.74726085e-03, -1.01933296e-03, -2.86432834e-05, 2.28111414e-05,\n", - " 4.02242178e-04, -3.13199876e-04, 1.53917062e+00, 2.45251037e-02,\n", - " 4.50183572e-05, 5.83105679e-05, 1.15511398e-04, -2.60952597e-03,\n", - " -9.70417246e-04, 1.52876906e-06, 5.43161032e-06, -1.02860232e-04,\n", - " -7.41358265e-05, 5.98019639e-08, 6.33276870e-05, 2.96344268e-07,\n", - " 5.91166499e-07, -1.46173592e-06, -4.48474858e-04, -5.03271939e-06,\n", - " -9.64945641e-06, 3.17631680e+00, -6.74609231e-04, 3.79257330e-02,\n", - " 4.48058789e-04, -4.14799637e-02, 3.04296494e-04, -6.79015121e-03,\n", - " -1.17146260e-05, 1.47111610e-07])" + "ic = np.array([ 3.84101549e-02, -8.29674554e-03, 3.04587364e-02, 2.80766373e-02,\n", + " -9.14885177e-03, -9.17520676e-04, -1.76115081e-02, 1.32010146e-02,\n", + " 1.62515224e-02, 1.08600254e-03, 1.53918671e+00, 4.13205067e-02,\n", + " -9.25169842e-04, 4.01449139e-03, 6.97326597e-03, -9.93383832e-03,\n", + " 8.88594931e-03, -6.07097456e-03, 4.34490969e-03, 4.19834122e-03,\n", + " -2.91974161e-03, 1.03085300e-05, 5.98444985e-04, -2.57753313e-05,\n", + " 5.22115566e-06, -3.01445438e-05, 3.26249104e-04, -1.92171554e-05,\n", + " 1.38469482e-05, 3.17552667e+00, 2.46854576e-03, 1.44249578e-01,\n", + " -5.94828283e-03, 2.34242352e-02, -3.08095487e-03, 9.15501463e-02,\n", + " 1.17932987e-03, -4.34659450e-05])" ] }, { @@ -496,6 +407,16 @@ "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "33661f84", + "metadata": {}, + "outputs": [], + "source": [ + "reference_traj_num[:, -1]" + ] + }, { "cell_type": "code", "execution_count": null, @@ -503,21 +424,31 @@ "metadata": {}, "outputs": [], "source": [ - "reference_traj[:, -1]" + "plt.figure(figsize=(12, 8))\n", + "\n", + "plt.plot(reference_traj_num[varx, :])\n", + "plt.plot(reference_traj[varx, :])\n", + "plt.show()" ] }, { "cell_type": "code", "execution_count": null, - "id": "69a02871", + "id": "8078daf2", "metadata": {}, "outputs": [], - "source": [] + "source": [ + "plt.figure(figsize=(12, 8))\n", + "\n", + "plt.plot(reference_traj_num[vary, :])\n", + "plt.plot(reference_traj[vary, :])\n", + "plt.show()" + ] }, { "cell_type": "code", "execution_count": null, - "id": "8d423db0", + "id": "6ed354fa", "metadata": {}, "outputs": [], "source": [] diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 3196af7..f39efff 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -119,7 +119,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con if return_symbolic_eqs: ret.append(eq_simplified) if return_symbolic_qgtensor: - ret.append(agotensor.tensor_dic) + ret.append(agotensor) return ret def translate_equations(equations, language='python'): diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index eb4358c..48a9831 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -53,7 +53,7 @@ class SymbolicTensorLinear(object): The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. """ - def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None, ): + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): self.atmospheric_inner_products = atmospheric_inner_products self.oceanic_inner_products = oceanic_inner_products @@ -812,7 +812,7 @@ def save_to_file(self, filename, **kwargs): pickle.dump(self.__dict__, f, **kwargs) f.close() - def subs_tensor(self, tensor=None, dict_opp=True, variables=None, remain_variables=dict()): + def sub_tensor(self, tensor=None, dict_opp=True, remain_variables=dict()): """ Uses sympy substitution to convert the symbolic tensor or a symbolic dictionary to a numerical one. @@ -822,9 +822,6 @@ def subs_tensor(self, tensor=None, dict_opp=True, variables=None, remain_variabl dict_opp: Boolian, if True, uses the stored tensor_dic object as the input - variables: dict of variable names to substitute - if None, all variables are substituted - remain_variables: dict of variables not to substitute if None all variables are substituted. This variable is the opposite of 'variables' """ @@ -832,17 +829,12 @@ def subs_tensor(self, tensor=None, dict_opp=True, variables=None, remain_variabl symbol_to_number_map = list() variables_not_found = list() - if variables is None: - for key in self.sym_params.keys(): - if key not in remain_variables: - try: - symbol_to_number_map.append(self.params.symbol_to_value[key]) - except: - variables_not_found.append(key) - - if len(variables_not_found) > 0: - print("The following variables were not found in the parameters: ") - print(variables_not_found) + for key in self.sym_params.keys(): + if key not in remain_variables: + try: + symbol_to_number_map.append(self.params.symbol_to_value[key]) + except: + variables_not_found.append(key) if isinstance(tensor, dict): ten_out = dict() From f4c09c8c57769da5f35f38a795b4276b61c3f376 Mon Sep 17 00:00:00 2001 From: ushham Date: Sat, 19 Aug 2023 18:55:01 +0200 Subject: [PATCH 045/143] Draft auto output --- qgs/functions/.cproto | 41 +++++ qgs/functions/.modelproto | 263 +++++++++++++++++++++++++++++++ qgs/functions/symbolic_output.py | 189 ++++++++++++++++++---- 3 files changed, 465 insertions(+), 28 deletions(-) create mode 100644 qgs/functions/.cproto create mode 100644 qgs/functions/.modelproto diff --git a/qgs/functions/.cproto b/qgs/functions/.cproto new file mode 100644 index 0000000..1cdb09b --- /dev/null +++ b/qgs/functions/.cproto @@ -0,0 +1,41 @@ +#Configuration files for the bifurcation diagram of ao model with temperature and wind stress. + +#Parameters name +# ! PARAMETERS +parnames = {1:'n',2:'epsa',3:'lambda',4:'coup',5:'d',6:'k',7:'kp',8:'Co',9:'Ca',11:'T',12,'theta',14:'t'} + +#Variables name +# ! VARIABLES +unames = {1:'psi1',2:'psi2',3:'psi3',4:'psi4',5:'psi5',6:'psi6',7:'psi7',8:'psi8',9:'psi9',10:'psi10',11:'theta0',12:'theta1',13:'theta2',14:'theta3',15:'theta4',16:'theta5',17:'theta6',18:'theta7',19:'theta8',20:'theta9',21:'theta10',22:'A1',23:'A2',24:'A3',25:'A4',26:'A5',27:'A6',28:'A7',29:'A8',30:'T0',31:'T1',32:'T2',33:'T3',34:'T4',35:'T5',36:'T6',37:'T7',38:'T8'} + +#Dimension of the system +# ! DIMENSION +NDIM= 38 + +#Problem type (1 for FP, 2 for PO, -2 for time integration) +IPS = 1 +#Start solution label +IRS = 0 +#Continuation parameters (in order of use) +# ! CONTINUATION ORDER +ICP = ['coup','epsa'] +#Number of mesh intervals +NTST= 100 +#Print and restart every NPR steps (0 to disable) +NPR= 0 +#Number of bifurcating branches to compute (negative number means continue only in one direction) +MXBF=0 +#Detection of Special Points +ISP=2 +#Maximum number of iteration in the Newton-Chord method +ITNW=7 +#Arc-length continuation parameters +DS = 0.00001, DSMIN= 1e-15, DSMAX= 1.0 +#Precision parameters (Typiq. EPSS = EPSL * 10^-2) +EPSL=1e-07, EPSU=1e-07, EPSS=1e-05 +#Number of parameter (don't change it) +NPAR = 36 +#User defined value where to save the solution +UZR={'coup': [0.007, 0.0075, 0.008, 0.0085, 0.009, 0.0095, 0.01, 0.0105, 0.011, 0.0115, 0.012, 0.0125, 0.013, 0.0135, 0.014, 0.0145, 0.015]} +#Stop conditions +UZSTOP = {'coup': [0.005, 0.02],'epsa': [0.5,1]} diff --git a/qgs/functions/.modelproto b/qgs/functions/.modelproto new file mode 100644 index 0000000..b8bd1dc --- /dev/null +++ b/qgs/functions/.modelproto @@ -0,0 +1,263 @@ +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- +! model_aot_VDDG.f90 - OAQGWFTS ocean-atmosphere coupled model (with temperatures) +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + +SUBROUTINE FUNC(NDIM,U,ICP,PAR,IJAC,F,DFDU,DFDP) + !--------- ---- + + ! Evaluates the algebraic equations or ODE right hand side + + ! Input arguments : + ! NDIM : Dimension of the algebraic or ODE system + ! U : State variables + ! ICP : Array indicating the free parameter(s) + ! PAR : Equation parameters + + ! Values to be returned : + ! F : Equation or ODE right hand side values + + ! Normally unused Jacobian arguments : IJAC, DFDU, DFDP (see manual) + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM, IJAC, ICP(*) + DOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) + DOUBLE PRECISION, INTENT(OUT) :: F(NDIM) + DOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM,NDIM),DFDP(NDIM,*) + + + +! PARAMETER DECLERATION + +! CONTINUATION PARAMETERS + + +! EVOLUTION EQUATIONS + +END SUBROUTINE FUNC + +!----------------------------------------------------------------------- +!----------------------------------------------------------------------- + +SUBROUTINE STPNT(NDIM,U,PAR,T) + !--------- ----- + + ! Input arguments : + ! NDIM : Dimension of the algebraic or ODE system + + ! Values to be returned : + ! U : A starting solution vector + ! PAR : The corresponding equation-parameter values + + ! Note : For time- or space-dependent solutions this subroutine has + ! the scalar input parameter T contains the varying time or space + ! variable value. + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM + DOUBLE PRECISION, INTENT(INOUT) :: U(NDIM),PAR(*) + DOUBLE PRECISION, INTENT(IN) :: T + DOUBLE PRECISION :: X(NDIM+1) + INTEGER :: i,is + + ! Initialize the equation parameters + +! INITIALISE PARAMETERS + + ! Initialize the solution + U = 0.0d0 + ! Initialization from a solution file (selection with PAR36) + ! open(unit=15,file='',status='old') + ! is=int(PAR(36)) + ! if (is.gt.0) print*, 'Loading from solution :',is + ! DO i=1,is + ! read(15,*) X + ! ENDDO + ! close(15) + ! U=X(2:NDIM+1) + + +END SUBROUTINE STPNT + +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + +SUBROUTINE BCND(NDIM,PAR,ICP,NBC,U0,U1,FB,IJAC,DBC) + !--------- ---- + + ! Boundary Conditions + + ! Input arguments : + ! NDIM : Dimension of the ODE system + ! PAR : Equation parameters + ! ICP : Array indicating the free parameter(s) + ! NBC : Number of boundary conditions + ! U0 : State variable values at the left boundary + ! U1 : State variable values at the right boundary + + ! Values to be returned : + ! FB : The values of the boundary condition functions + + ! Normally unused Jacobian arguments : IJAC, DBC (see manual) + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM, ICP(*), NBC, IJAC + DOUBLE PRECISION, INTENT(IN) :: PAR(*), U0(NDIM), U1(NDIM) + DOUBLE PRECISION, INTENT(OUT) :: FB(NBC) + DOUBLE PRECISION, INTENT(INOUT) :: DBC(NBC,*) + + !X FB(1)= + !X FB(2)= + +END SUBROUTINE BCND + +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + +SUBROUTINE ICND(NDIM,PAR,ICP,NINT,U,UOLD,UDOT,UPOLD,FI,IJAC,DINT) + + ! Integral Conditions + + ! Input arguments : + ! NDIM : Dimension of the ODE system + ! PAR : Equation parameters + ! ICP : Array indicating the free parameter(s) + ! NINT : Number of integral conditions + ! U : Value of the vector function U at `time' t + + ! The following input arguments, which are normally not needed, + ! correspond to the preceding point on the solution branch + ! UOLD : The state vector at 'time' t + ! UDOT : Derivative of UOLD with respect to arclength + ! UPOLD : Derivative of UOLD with respect to `time' + + ! Normally unused Jacobian arguments : IJAC, DINT + + ! Values to be returned : + ! FI : The value of the vector integrand + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM, ICP(*), NINT, IJAC + DOUBLE PRECISION, INTENT(IN) :: PAR(*) + DOUBLE PRECISION, INTENT(IN) :: U(NDIM), UOLD(NDIM), UDOT(NDIM), UPOLD(NDIM) + DOUBLE PRECISION, INTENT(OUT) :: FI(NINT) + DOUBLE PRECISION, INTENT(INOUT) :: DINT(NINT,*) + +END SUBROUTINE ICND + +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + + +SUBROUTINE FOPT(NDIM,U,ICP,PAR,IJAC,FS,DFDU,DFDP) + !--------- ---- + ! + ! Defines the objective function for algebraic optimization problems + ! + ! Supplied variables : + ! NDIM : Dimension of the state equation + ! U : The state vector + ! ICP : Indices of the control parameters + ! PAR : The vector of control parameters + ! + ! Values to be returned : + ! FS : The value of the objective function + ! + ! Normally unused Jacobian argument : IJAC, DFDP + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM, ICP(*), IJAC + DOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) + DOUBLE PRECISION, INTENT(OUT) :: FS + DOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM),DFDP(*) + +END SUBROUTINE FOPT + +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + +SUBROUTINE PVLS(NDIM,U,PAR) + !--------- ---- + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM + DOUBLE PRECISION, INTENT(INOUT) :: U(NDIM) + DOUBLE PRECISION, INTENT(INOUT) :: PAR(*) + DOUBLE PRECISION :: GETP,pi,realfm,imagfm,imagfm1 + DOUBLE PRECISION :: lw,lw1 + LOGICAL, SAVE :: first = .TRUE. + DOUBLE PRECISION :: T + INTEGER :: i + + IF (first) THEN + CALL STPNT(NDIM,U,PAR,T) + first = .FALSE. + ENDIF + + !PAR(26)=U(44) + !PAR(27)=U(52) + PAR(25)=0. + pi = 4*ATAN(1d0) + i=1 + lw=100. + lw1=101. + DO WHILE(i < NDIM) + realfm = GETP('EIG',I*2-1,U) + IF (ABS(realfm) < lw) THEN + lw = ABS(realfm) + lw1 = ABS(GETP('EIG',(I+1)*2-1,U)) + imagfm1 = ABS(GETP('EIG',(I+1)*2,U)) + imagfm = ABS(GETP('EIG',I*2,U)) + END IF + i=i+1 + END DO + IF ((lw==lw1).AND.(imagfm1==imagfm).AND.(imagfm/=0.D0)) THEN + PAR(25) = 2*pi/imagfm + ENDIF + !---------------------------------------------------------------------- + ! NOTE : + ! Parameters set in this subroutine should be considered as ``solution + ! measures'' and be used for output purposes only. + ! + ! They should never be used as `true'' continuation parameters. + ! + ! They may, however, be added as ``over-specified parameters'' in the + ! parameter list associated with the AUTO-Constant NICP, in order to + ! print their values on the screen and in the ``p.xxx file. + ! + ! They may also appear in the list associated with AUTO-Constant NUZR. + ! + !---------------------------------------------------------------------- + ! For algebraic problems the argument U is, as usual, the state vector. + ! For differential equations the argument U represents the approximate + ! solution on the entire interval [0,1]. In this case its values must + ! be accessed indirectly by calls to GETP, as illustrated below. + !---------------------------------------------------------------------- + ! + ! Set PAR(2) equal to the L2-norm of U(1) + !X PAR(2)=GETP('NRM',1,U) + ! + ! Set PAR(3) equal to the minimum of U(2) + !X PAR(3)=GETP('MIN',2,U) + ! + ! Set PAR(4) equal to the value of U(2) at the left boundary. + !X PAR(4)=GETP('BV0',2,U) + ! + ! Set PAR(5) equal to the pseudo-arclength step size used. + !X PAR(5)=GETP('STP',1,U) + ! + !---------------------------------------------------------------------- + ! The first argument of GETP may be one of the following: + ! 'NRM' (L2-norm), 'MAX' (maximum), + ! 'INT' (integral), 'BV0 (left boundary value), + ! 'MIN' (minimum), 'BV1' (right boundary value). + ! + ! Also available are + ! 'STP' (Pseudo-arclength step size used). + ! 'FLD' (`Fold function', which vanishes at folds). + ! 'BIF' (`Bifurcation function', which vanishes at singular points). + ! 'HBF' (`Hopf function'; which vanishes at Hopf points). + ! 'SPB' ( Function which vanishes at secondary periodic bifurcations). + !---------------------------------------------------------------------- +END SUBROUTINE PVLS \ No newline at end of file diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index f39efff..0c91e6b 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -8,6 +8,8 @@ from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT, SymbolicTensorT4 +import os + python_lang_translation = { 'sqrt': 'math.sqrt', 'pi': 'math.pi' @@ -15,7 +17,7 @@ fortran_lang_translation = { '**': '^' - #TODO: Is there a reason that sqrt is replaced with sq2 in auto? For computational speedup? + #TODO: may need to add variable for pi } julia_lang_translation = { @@ -182,34 +184,19 @@ def format_equations(equations, params, save_loc=None, language='python', remain remain_variables: Set or list of strings A list or set of variables not to substitute. Only is used when variables is set to True. - ''' - # Substitute variables equation_dict = dict() - if isinstance(remain_variables, (set, list, dict)): - # make a dictionary of variables to substitute from parameters - sub_vals = dict() - for key in params.symbol_to_value.keys(): - if len(remain_variables) == 0: - sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] - else: - if key not in remain_variables: - sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] - elif remain_variables is None: - sub_vals = None + sub_vals = _sub_values(params, remain_variables) - else: - raise ValueError("Incorrect type for substitution, needs to be set, list, or dict of strings, not: " + str(type(remain_variables))) - # Substitute variable symbols vector_subs = dict() if language == 'python': for i in range(1, params.ndim+1): vector_subs['U_'+str(i)] = sy.Symbol('U['+str(i-1)+']') - if language == 'fortran': + if language == 'fortran' or language == 'auto': for i in range(1, params.ndim+1): vector_subs['U_'+str(i)] = sy.Symbol('U('+str(i)+')') @@ -289,37 +276,183 @@ def equation_as_function(equations, params, string_output=False, language='pytho f_output.append('function f(t, U, kwargs...)') f_output.append('\t#Tendency function of the qgs model') f_output.append('\tU_out = similar(U)') + for v in free_vars: + f_output.append('\t' + str(v) + " = kwargs['" + str(v) + "']") + for n, eq in enumerate(eq_dict.values()): f_output.append('\tF['+str(n+1)+'] = ' + str(eq)) f_output.append('\treturn F') f_output.append('end') - #//TODO: Add statement for Fortran - #//TODO: Add statement for mathematica + if language == 'fortran': + eq_dict = translate_equations(eq_dict, language='fortran') + + f_output = list() + f_var = '' + if len(free_vars) > 0: + for fv in free_vars: + f_var += str(fv) + ', ' + f_output.append('SUBROUTINE FUNC(NDIM, t, U, F, ' + f_var[:-2] + ')') + else: + f_output.append('SUBROUTINE FUNC(NDIM, t, U, F)') + + f_output.append('\t!Tendency function of the qgs model') + f_output.append('\tINTEGER, INTENT(IN) :: NDIM') + f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*)') + f_output.append('\tDOUBLE PRECISION, INTENT(OUT) :: F(NDIM)') + + for v in free_vars: + f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: ' + str(v)) + + f_output.append('') + + f_output = _split_equations(eq_dict, f_output) + + f_output.append('END SUBROUTINE') + + if language == 'auto': + eq_dict = translate_equations(eq_dict, language='fortran') + + f_output = list() + eq_dict = _split_equations(eq_dict, f_output) + f_output = create_auto_file(eq_dict, params, free_vars) + + if language == 'mathematica': + #TODO: This function needs testing before release + eq_dict = translate_equations(eq_dict, language='mathematica') + + f_output = list() + f_output.append('F = Array[' + str(len(eq_dict)) + ']') + + for n, eq in enumerate(eq_dict.values()): + f_output.append('F['+str(n+1)+'] = ' + str(eq)) + + #TODO !!!! Killing output as I have no confidence in the above code !!!! + f_output = None return f_output -def equation_to_auto(equations, params, remain_variables=dict()): +def create_auto_file(equations, params, free_variables, remain_variables={}): # User passes the equations, with the variables to leave as variables. # The existing model parameters are used to populate the auto file # The variables given as `remain_variables` remain in the equations. # There is a limit of 1-10 remian variables + base_path = os.path.dirname(__file__) + base_file = '.modelproto' if (len(remain_variables) < 1) or (len(remain_variables) > 10): ValueError("Too many variables for auto file") - str_equations, free_variables = format_equations(equations=equations, params=params, language='fortran', variables=True) + # Declare variables + declare_var = list() + for i in free_variables: + declare_var.append('DOUBLE PRECISION ' + str(i)) - natm, nog = params.nmod - dim = params.ndim - offset = 1 if params.dynamic_T else 0 - - # make list of free variables + # make list of parameters var_list = list() + var_ini = list() + + sub_vals = _sub_values(params, remain_variables) + for i, fv in enumerate(free_variables): temp_str = "PAR(" + str(i) + ") = " + str(fv) + + initial_value = "PAR(" + str(i) + ") = " + str(sub_vals[fv]) + " Variable: " + str(fv) + var_list.append(temp_str) + var_ini.append(initial_value) + + # Open base file and input strings + f_base = open(base_path + '/' + base_file, 'r') + lines = f_base.readlines() + f_base.close() + + auto_file = list() + #TODO: Tabs not working here correctly + for ln in lines: + if 'PARAMETER DECLERATION' in ln: + for dv in declare_var: + auto_file.append('\t' + dv) + elif 'CONTINUATION PARAMETERS' in ln: + for v in var_list: + auto_file.append('\t' + v) + elif 'EVOLUTION EQUATIONS' in ln: + for e in equations: + auto_file.append('\t' + e) + elif 'INITIALISE PARAMETERS' in ln: + for iv in var_ini: + auto_file.append('\t' + iv) + else: + auto_file.append(ln.replace('\n', '')) + + return auto_file + + +def _sub_values(params, remain_variables): + # Substitute variables + if isinstance(remain_variables, (set, list, dict)): + # make a dictionary of variables to substitute from parameters + sub_vals = dict() + for key in params.symbol_to_value.keys(): + if len(remain_variables) == 0: + sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] + else: + if key not in remain_variables: + sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] + + elif remain_variables is None: + sub_vals = None + else: + raise ValueError("Incorrect type for substitution, needs to be set, list, or dict of strings, not: " + str(type(remain_variables))) + + return sub_vals + +def _split_equations(eq_dict, f_output, line_len=80): + ''' + Function to split FORTRAN equaitons to a set length when producing functions + ''' + for n, eq in enumerate(eq_dict.values()): + # split equaitons to be a maximum of `line_len` + + #split remainder of equation into chunkcs of length `line_length` + eq_chunks = [eq[x: x + line_len] for x in range(0, len(eq), line_len)] + f_output.append('F('+str(n+1)+') =\t ' + eq_chunks[0] + "&") + for ln in eq_chunks[1:-1]: + f_output.append("\t&" + ln + "&") + + f_output.append("\t&" + eq_chunks[-1]) + f_output.append('') + return f_output + +def _variable_names(params): + # Function to make the variable names for auto + num_v = params.number_of_variables + offset = 1 if params.number_of_variables else 0 + + var_list = list() + if params.atmospheric_basis is not None: + for i in range(num_v[0]): + var_list.append('psi' + str(i)) + + for i in range(offset, num_v[1]+offset): + var_list.append('theta' + str(i)) + + if params.ground_basis is not None: + for i in range(offset, num_v[2] + offset): + var_list.append('gT' + str(i)) + + if params.oceanic_basis is not None: + for i in range(num_v[2]): + var_list.append('A' + str(i)) + + for i in range(offset, num_v[3] + offset): + var_list.append('T' + str(i)) + + print(var_list) + output = dict() + for i, v in enumerate(var_list): + output[i+1] = v - \ No newline at end of file + return output \ No newline at end of file From 8e77fae7c1bb15137a3387e57ca75601f43c0a52 Mon Sep 17 00:00:00 2001 From: ushham Date: Sat, 19 Aug 2023 22:59:04 +0200 Subject: [PATCH 046/143] Addition of some documentation --- qgs/functions/symbolic_output.py | 81 ++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 4 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 0c91e6b..c5d35eb 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -32,7 +32,45 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables={}, language='python', return_inner_products=False, return_jacobian=False, return_symbolic_eqs=False, return_symbolic_qgtensor=False): """ Function to output the raw symbolic functions of the qgs model. + + Parameters + ---------- + params: QGParams + The parameters fully specifying the model configuration. + atm_ip: SymbolicAtmosphericInnerProducts, optional + Allows for stored inner products to be input + ocn_ip: SymbolicOceanInnerProducts, optional + Allows for stored inner products to be input + gnd_ip: SymbolicGroundInnerProducts, optional + Allows for stored inner products to be input + continuation_variables: Set or List or None + The variables to not substitute and to leave in the equations, if None no variables are substituted + language: String + Options for the output language syntax: 'python', 'julia', 'fortran', 'auto', 'mathematica' + return_inner_products: bool + If True, return the inner products of the model. Default to False. + return_jacobian: bool + If True, return the Jacobian of the model. Default to False. + return_symbolic_eqs: bool + If True, return the substituted symbolic equations + return_symbolic_qgtensor: bool + If True, return the symbolic tendencies tensor of the model. Default to False. + + Returns + ------- + funcs: string + The substituted functions in the language syntax specified, as a string + Deq_simplified: symbolic equations + Dict of the substituted Jacobian matrix + inner_products: (SymbolicAtmosphericInnerProducts, SymbolicOceanicInnerProducts, SymbolicGroundInnerProducts) + If `return_inner_products` is True, the inner products of the system. + eq_simplified: Symbolic equations + If `return_symbolic_eqs` is True, Dict of the model tendencies symbolic functions + agotensor: SymbolicQgsTensor + If `return_symbolic_qgtensor` is True, the symbolic tendencies tensor of the system. + """ + if 'n' in continuation_variables: make_ip_subs = False warnings.warn("Calculating innerproducts symbolically, as the variable 'n' has been specified as a variable, this takes several minutes.") @@ -126,7 +164,13 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con def translate_equations(equations, language='python'): ''' - Function to output the model equations as a string in the specified language. The languages that are availible to the user are: + Function to output the model equations as a string in the specified language syntax. + + Parameters + ---------- + equations: dict + Symbolic + - Python - Fortran - Julia @@ -316,8 +360,8 @@ def equation_as_function(equations, params, string_output=False, language='pytho f_output = list() eq_dict = _split_equations(eq_dict, f_output) - f_output = create_auto_file(eq_dict, params, free_vars) - + create_auto_file(eq_dict, params, free_vars) + if language == 'mathematica': #TODO: This function needs testing before release eq_dict = translate_equations(eq_dict, language='mathematica') @@ -334,12 +378,16 @@ def equation_as_function(equations, params, string_output=False, language='pytho return f_output def create_auto_file(equations, params, free_variables, remain_variables={}): + + #TODO: Find out best way to save these files + # User passes the equations, with the variables to leave as variables. # The existing model parameters are used to populate the auto file # The variables given as `remain_variables` remain in the equations. # There is a limit of 1-10 remian variables base_path = os.path.dirname(__file__) base_file = '.modelproto' + base_config = '.cproto' if (len(remain_variables) < 1) or (len(remain_variables) > 10): ValueError("Too many variables for auto file") @@ -363,6 +411,8 @@ def create_auto_file(equations, params, free_variables, remain_variables={}): var_list.append(temp_str) var_ini.append(initial_value) + ###### Writing model file ################ + # Open base file and input strings f_base = open(base_path + '/' + base_file, 'r') lines = f_base.readlines() @@ -386,8 +436,31 @@ def create_auto_file(equations, params, free_variables, remain_variables={}): else: auto_file.append(ln.replace('\n', '')) - return auto_file + print('\n'.join(auto_file)) + + ###### Writing config file ################ + + c_base = open(base_path + '/' + base_config, 'r') + lines = c_base.readlines() + c_base.close() + + auto_config = list() + for ln in lines: + if '! PARAMETERS' in ln: + auto_config.append('parnames = ' + str({i+1: v for i, v in enumerate(free_variables)})) + + elif '! VARIABLES' in ln: + auto_config.append('unames = ' + str(_variable_names(params))) + + elif '! CONTINUATION ORDER' in ln: + auto_config.append('ICP = ' + str(list(free_variables))) + + else: + auto_config.append(ln.replace('\n', '')) + + print('\n'.join(auto_config)) + return equations def _sub_values(params, remain_variables): # Substitute variables From 6ff0df5275d08e967e566f61eacc119c789f1425 Mon Sep 17 00:00:00 2001 From: ushham Date: Sun, 20 Aug 2023 13:40:57 +0200 Subject: [PATCH 047/143] Bug fixes and addition of some documentation --- qgs/functions/.cproto | 2 +- qgs/functions/symbolic_mul.py | 90 +++++++++++++++++- qgs/functions/symbolic_output.py | 158 ++++++++++++++++++++----------- qgs/inner_products/symbolic.py | 32 ------- qgs/tensors/symbolic_qgtensor.py | 15 ++- 5 files changed, 203 insertions(+), 94 deletions(-) diff --git a/qgs/functions/.cproto b/qgs/functions/.cproto index 1cdb09b..66b3595 100644 --- a/qgs/functions/.cproto +++ b/qgs/functions/.cproto @@ -1,4 +1,4 @@ -#Configuration files for the bifurcation diagram of ao model with temperature and wind stress. +#Configuration files #Parameters name # ! PARAMETERS diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py index 758634f..6dc5376 100644 --- a/qgs/functions/symbolic_mul.py +++ b/qgs/functions/symbolic_mul.py @@ -1,6 +1,11 @@ import sympy as sy def _add_to_dict(dic, loc, value): + ''' + Adds `value` to dictionary `dic`, with the dictionary key of `loc`. + If the dictionary did not have a key of `loc` before, a new key is made. + ''' + try: dic[loc] += value except: @@ -41,7 +46,22 @@ def _symbolic_tensordot(a, b, axes=2): return sy.tensorcontraction(prod, sum_cols) def symbolic_sparse_mult2(dic, vec_a): - #//TODO: Complete documentation + """ + Symbolic multiplication of a tensor with one vector: + :math:`A_{i,j} = {\displaystyle \sum_{k=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k} \, a_k` + + Parameters + ---------- + dic: Dict(Sympy.Symbol) + A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the tensor. + vec_a: List(Sympy.Symbol) + The list :math:`a_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + + Returns + ------- + res: dict + The matrix :math:`A_{i,j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. + """ res = dict() for key in dic.keys(): @@ -52,7 +72,26 @@ def symbolic_sparse_mult2(dic, vec_a): return res def symbolic_sparse_mult3(dic, vec_a, vec_b): - #//TODO: Complete documentation + """ + Symbolic multiplication of a tensor with two vectors: + :math:`v_i = {\displaystyle \sum_{j,k=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k} \, a_j \, b_k` + + Parameters + ---------- + dic: Dict(Sympy.Symbol) + A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the tensor. + vec_a: List(Sympy.Symbol) + The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_b: List(Sympy.Symbol) + The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + + + Returns + ------- + res: List(Sympy.Symbol) + The vector :math:`v_i`, of shape (:attr:`~.params.QgParams.ndim` + 1,), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. + + """ res = dict() for key in dic.keys(): @@ -63,7 +102,29 @@ def symbolic_sparse_mult3(dic, vec_a, vec_b): return res def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): - #//TODO: Complete documentation + """ + Symbolic multiplication of a rank-5 tensor with three vectors: + :math:`A_{i, j} = {\displaystyle \sum_{k,l,m=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k,l, m} \, a_k \, b_l \, c_m` + + + Parameters + ---------- + dic: Dict(Sympy.Symbol) + A dictionary where they keys are a tuple of 5 elements which are the coordinates of the tensor values, which are contained in the dictionary values. + value: ~numpy.ndarray(float) + A 1D array of shape (n_elems,), a list of value in the tensor + vec_a: List(Sympy.Symbol) + The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_b: List(Sympy.Symbol) + The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_c: List(Sympy.Symbol) + The list :math:`c_l` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + + Returns + ------- + res: List(Sympy.Symbol) + The matrix :math:`A_{i, j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. + """ res = dict() for key in dic.keys(): @@ -74,7 +135,28 @@ def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): return res def symbolic_sparse_mult5(dic, vec_a, vec_b, vec_c, vec_d): - #//TODO: Complete documentation + """ + Symbolic multiplication of a rank-5 tensor with four vectors: + :math:`v_i = {\displaystyle \sum_{j,k,l,m=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k,l,m} \, a_j \, b_k \, c_l \, d_m` + + Parameters + ---------- + dic: Dict(Sympy.Symbol) + A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the tensor. + vec_a: List(Sympy.Symbol) + The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_b: List(Sympy.Symbol) + The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_c: List(Sympy.Symbol) + The list :math:`c_l` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_d: List(Sympy.Symbol) + The list :math:`d_m` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + + Returns + ------- + res: List(Sympy.Symbol) + The vector :math:`v_i`, of shape (:attr:`~.params.QgParams.ndim` + 1,), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. + """ res = dict() for key in dic.keys(): diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index c5d35eb..a40f487 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -70,12 +70,16 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con If `return_symbolic_qgtensor` is True, the symbolic tendencies tensor of the system. """ - - if 'n' in continuation_variables: + if continuation_variables is None: make_ip_subs = False - warnings.warn("Calculating innerproducts symbolically, as the variable 'n' has been specified as a variable, this takes several minutes.") else: - make_ip_subs = True + if 'n' in continuation_variables: + make_ip_subs = False + else: + make_ip_subs = True + + if not(make_ip_subs): + warnings.warn("Calculating innerproducts symbolically, as the variable 'n' has been specified as a variable, this takes several minutes.") if params.atmospheric_basis is not None: if atm_ip is None: @@ -148,7 +152,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con if return_jacobian: Deq_simplified = Deq - funcs = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, remain_variables=continuation_variables) + funcs = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, free_variables=continuation_variables) ret = list() ret.append('\n'.join(funcs)) @@ -169,12 +173,19 @@ def translate_equations(equations, language='python'): Parameters ---------- equations: dict - Symbolic - - - Python - - Fortran - - Julia - - Mathematica + Dictinary of the symbolic model equations + language: string + Language syntax that the equations are returned in. Options are: + - `python` + - `fortran` + - `julia` + - `auto` + - `mathematica` + + Returns + ------- + str_eq: dict + dict of strings of the model equations ''' if language == 'python': @@ -203,36 +214,43 @@ def translate_equations(equations, language='python'): return str_eq -def format_equations(equations, params, save_loc=None, language='python', remain_variables={}, print_equations=False): +def format_equations(equations, params, save_loc=None, language='python', free_variables={}, print_equations=False): ''' - Function formats the equations, in the programming language specified, and saves the equations to the specified location. - The variables in the equation are substituted if the model variable is input. - - Parameters - ---------- - equations: dictionary of symbolic expressions + Function formats the equations, in the programming language specified, and saves the equations to the specified location. + The variables in the equation are substituted if the model variable is input. - params: qgs model params - - save_loc: String, location to save the outputs as a .txt file - - language: String, programming language to output the strings as - - variables: Set or list of Strings, dict of sympy.Symbol with corrisponding values, or Bool - - If a set or list of strings is input, the corrisponding value in the parameters is found - - if a dict of sympy.Symbols is input, this is used to substitute the tensor + Parameters + ---------- + equations: Dict + Dictionary of symbolic model equations + params: QGParams + qgs model params + save_loc: String + location to save the outputs as a .txt file + language: string + Language syntax that the equations are returned in. Options are: + - `python` + - `fortran` + - `julia` + - `auto` + - `mathematica` + free_variables: Set or list or None + The variables to not substitute and to leave in the equations, if None no variables are substituted + print_equations: bool + If True, equations are printed by the function, if False, equation string is returned by the function. Defaults to False - if True is passed, the parameters are used to substitute all variables - - remain_variables: Set or list of strings - A list or set of variables not to substitute. Only is used when variables is set to True. + Returns + ------- + equation_dict: Dict + Dictionary of symbolic model equations, that have been substituted with numerical values + + free_vars: Set + Set of strings of model variables that have not been substitued in this function, and remain as variabes in the equaitons. ''' equation_dict = dict() - sub_vals = _sub_values(params, remain_variables) + sub_vals = _sub_values(params, free_variables) # Substitute variable symbols vector_subs = dict() @@ -284,14 +302,35 @@ def format_equations(equations, params, save_loc=None, language='python', remain else: return equation_dict, free_vars -def equation_as_function(equations, params, string_output=False, language='python', remain_variables={}): +def equation_as_function(equations, params, string_output=True, language='python', free_variables={}): + ''' + Converts the symbolic equations to a function in string format in the language syntax specified, or a lambdified python function + + Parameters + ---------- + equations: Dict + Dictionary of the substituted symbolic model equations + params: QGParams + The parameters fully specifying the model configuration. + string_output: bool + If True, returns a lambdified python function, if False returns a string function, defaults to True + free_variables: Set or List or None + Variables that are not substituted with numerical values. If None, no symbols are substituted + + + Returns + ------- + f_output: lambdified python function, or String + If string_output is True, output is a funciton in the specified language syntax, if False the output is a lambdified python function + + ''' - eq_dict, free_vars = format_equations(equations, params, language=language, remain_variables=remain_variables) + eq_dict, free_vars = format_equations(equations, params, language=language, free_variables=free_variables) + f_output = list() if language == 'python': if string_output: - f_output = list() f_output.append('def f(t, U, **kwargs):') f_output.append('\t#Tendency function of the qgs model') f_output.append('\tF = np.empty_like(U)') @@ -316,7 +355,6 @@ def equation_as_function(equations, params, string_output=False, language='pytho if language == 'julia': eq_dict = translate_equations(eq_dict, language='julia') - f_output = list() f_output.append('function f(t, U, kwargs...)') f_output.append('\t#Tendency function of the qgs model') f_output.append('\tU_out = similar(U)') @@ -332,7 +370,6 @@ def equation_as_function(equations, params, string_output=False, language='pytho if language == 'fortran': eq_dict = translate_equations(eq_dict, language='fortran') - f_output = list() f_var = '' if len(free_vars) > 0: for fv in free_vars: @@ -358,7 +395,6 @@ def equation_as_function(equations, params, string_output=False, language='pytho if language == 'auto': eq_dict = translate_equations(eq_dict, language='fortran') - f_output = list() eq_dict = _split_equations(eq_dict, f_output) create_auto_file(eq_dict, params, free_vars) @@ -366,7 +402,6 @@ def equation_as_function(equations, params, string_output=False, language='pytho #TODO: This function needs testing before release eq_dict = translate_equations(eq_dict, language='mathematica') - f_output = list() f_output.append('F = Array[' + str(len(eq_dict)) + ']') for n, eq in enumerate(eq_dict.values()): @@ -377,19 +412,33 @@ def equation_as_function(equations, params, string_output=False, language='pytho return f_output -def create_auto_file(equations, params, free_variables, remain_variables={}): +def create_auto_file(equations, params, free_variables): + ''' + Creates the auto configuration file and the model file. + Saves files to specified folder. + + Parameters + ---------- + equations: Dict + Dictionary of the substituted symbolic model equations + params: QGParams + The parameters fully specifying the model configuration. + free_variables: Set or List or None + Variables that are not substituted with numerical values. If None, no symbols are substituted + + ''' #TODO: Find out best way to save these files # User passes the equations, with the variables to leave as variables. # The existing model parameters are used to populate the auto file - # The variables given as `remain_variables` remain in the equations. + # The variables given as `free_variables` remain in the equations. # There is a limit of 1-10 remian variables base_path = os.path.dirname(__file__) base_file = '.modelproto' base_config = '.cproto' - if (len(remain_variables) < 1) or (len(remain_variables) > 10): + if (len(free_variables) < 1) or (len(free_variables) > 10): ValueError("Too many variables for auto file") # Declare variables @@ -401,7 +450,7 @@ def create_auto_file(equations, params, free_variables, remain_variables={}): var_list = list() var_ini = list() - sub_vals = _sub_values(params, remain_variables) + sub_vals = _sub_values(params, free_variables) for i, fv in enumerate(free_variables): temp_str = "PAR(" + str(i) + ") = " + str(fv) @@ -462,25 +511,24 @@ def create_auto_file(equations, params, free_variables, remain_variables={}): return equations -def _sub_values(params, remain_variables): +def _sub_values(params, free_variables): # Substitute variables - if isinstance(remain_variables, (set, list, dict)): + if isinstance(free_variables, (set, list, dict)): # make a dictionary of variables to substitute from parameters sub_vals = dict() for key in params.symbol_to_value.keys(): - if len(remain_variables) == 0: + if len(free_variables) == 0: sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] else: - if key not in remain_variables: + if key not in free_variables: sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] - elif remain_variables is None: + elif free_variables is None: sub_vals = None else: - raise ValueError("Incorrect type for substitution, needs to be set, list, or dict of strings, not: " + str(type(remain_variables))) + raise ValueError("Incorrect type for substitution, needs to be set, list, or dict of strings, not: " + str(type(free_variables))) - return sub_vals def _split_equations(eq_dict, f_output, line_len=80): ''' @@ -491,11 +539,11 @@ def _split_equations(eq_dict, f_output, line_len=80): #split remainder of equation into chunkcs of length `line_length` eq_chunks = [eq[x: x + line_len] for x in range(0, len(eq), line_len)] - f_output.append('F('+str(n+1)+') =\t ' + eq_chunks[0] + "&") + f_output.append('\tF('+str(n+1)+') =\t ' + eq_chunks[0] + "&") for ln in eq_chunks[1:-1]: - f_output.append("\t&" + ln + "&") + f_output.append("\t\t&" + ln + "&") - f_output.append("\t&" + eq_chunks[-1]) + f_output.append("\t\t&" + eq_chunks[-1]) f_output.append('') return f_output diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index c5f1beb..74294cb 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -880,7 +880,6 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None num_threads = cpu_count() with Pool(max_workers=num_threads) as pool: - #//TODO: Check if this needs to be edited for symbolic ip if self.mk_subs: subs = self.subs + self.oceanic_basis.substitutions + self.atmospheric_basis.substitutions else: @@ -1601,7 +1600,6 @@ def _num_apply(ls): else: num_integrand = integrand[0] - # TODO: sy.integrate.ddquad says that the function needs to be f(y, x), not f(x, y)? func = sy.lambdify((integrand[1][0], integrand[2][0]), num_integrand, 'numpy') try: @@ -1708,36 +1706,6 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False if return_dict: return destination -def _symbolic_compute(pool, args_list, subs, destination, timeout, permute=False): - result_list = dict() - #//TODO: This function needs checking, I have stripped alot of the funcitionality out of _parallell_compute - future = pool.map(_apply, args_list, timeout=None) - results = future.result() - i = 0 - while True: - try: - res = next(results) - expr = res[1].simplify() - if subs is not None: - result_list[res[0]] = expr.subs(subs) - else: - result_list[res[0]] = expr - - if permute: - i = res[0][0] - idx = res[0][1:] - perm_idx = multiset_permutations(idx) - for perm in perm_idx: - idx = [i] + perm - if subs is not None: - result_list[tuple(idx)] = expr.subs(subs) - else: - result_list[tuple(idx)] = expr - - except StopIteration: - break - return result_list - if __name__ == '__main__': from qgs.params.params import QgParams pars = QgParams(dynamic_T=True) # , T4=True) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 48a9831..bf41302 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -750,7 +750,6 @@ def _set_symbolic_tensor(self): else: dims = (ndim + 1, ndim + 1, ndim + 1) - #//TODO: I needed to make copies here, but I am not sure why. If I tried to input the dict without the copy it messed up the keys jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jac_dic.copy(), dims) tensor = sy.tensor.array.ImmutableSparseNDimArray(self.tensor_dic.copy(), dims) @@ -788,7 +787,6 @@ def jacobian_from_dict(dic): @staticmethod def simplify_dict(dic): - # this is not correct, I should not permute the tuple, but sort the list of them keys = dic.keys() dic_upp = dict() @@ -824,6 +822,11 @@ def sub_tensor(self, tensor=None, dict_opp=True, remain_variables=dict()): remain_variables: dict of variables not to substitute if None all variables are substituted. This variable is the opposite of 'variables' + + Returns + ------- + ten_out: dict + Dictionary of the substituted tensor of the model tendencies, with coordinates and value """ symbol_to_number_map = list() @@ -863,6 +866,14 @@ def sub_tensor(self, tensor=None, dict_opp=True, remain_variables=dict()): return ten_out def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): + ''' + Print the non-zero coordinates of values of the tensor of the model tendencies + + Parameters + ---------- + tensor: dict or Sympy ImmutableSparseNDimArray + Tensor of model tendencies, either as a dictionary with keys of non-zero coordinates, and values of Sympy Symbols or floats, or as a ImmutableSparseNDimArray. + ''' if tensor is None: if dict_opp: temp_ten = self.tensor_dic From c8257f11f21aefa2483c80110ea515e6331d3ddf Mon Sep 17 00:00:00 2001 From: ushham Date: Sun, 20 Aug 2023 16:47:57 +0200 Subject: [PATCH 048/143] Notebooks tested --- .../Symbolic Output-Linear-groud test.ipynb | 26 +----------------- notebooks/Symbolic Output-Linear.ipynb | 24 ++++++++++++----- notebooks/Symbolic Output-dynamic_T.ipynb | 27 +++++++++---------- 3 files changed, 32 insertions(+), 45 deletions(-) diff --git a/notebooks/Symbolic Output-Linear-groud test.ipynb b/notebooks/Symbolic Output-Linear-groud test.ipynb index f20eb3a..d843030 100644 --- a/notebooks/Symbolic Output-Linear-groud test.ipynb +++ b/notebooks/Symbolic Output-Linear-groud test.ipynb @@ -145,30 +145,6 @@ "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=True) # <- Can be turned off once saved" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea85360f", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True) \n", - "gnd_loaded = GroundSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "196d5440", - "metadata": {}, - "outputs": [], - "source": [ - "atm_loaded.load_from_file('temp_atm_sym_lin_gnd_test.ip')\n", - "gnd_loaded.load_from_file('temp_gnd_sym_lin_gnd_test.ip')" - ] - }, { "cell_type": "markdown", "id": "d43d341b", @@ -290,7 +266,7 @@ "\tF[27] = 0.0172043158333333*U[17] - 0.00976477713178294*U[27]\n", "\tF[28] = 0.0172043158333333*U[18] - 0.00976477713178294*U[28]\n", "\tF[29] = 0.0172043158333333*U[19] - 0.00976477713178294*U[29]\n", - "\treturn F\n" + "\treturn F" ] }, { diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 9fef77c..acd036a 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -117,15 +117,17 @@ "metadata": {}, "outputs": [], "source": [ - "atm_loaded.load_from_file('temp_atm_sym_lin.ip')\n", - "ocn_loaded.load_from_file('temp_ocn_sym_lin.ip')" + "# atm_loaded.load_from_file('temp_atm_sym_lin.ip')\n", + "# ocn_loaded.load_from_file('temp_ocn_sym_lin.ip')" ] }, { "cell_type": "code", "execution_count": null, "id": "0165d8ea", - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "model_parameters.print_params()" @@ -167,7 +169,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, = create_symbolic_equations(model_parameters, continuation_variables={'eps'})" + "funcs, = create_symbolic_equations(model_parameters, continuation_variables={'eps'}, language='python')" ] }, { @@ -175,7 +177,7 @@ "execution_count": null, "id": "5fd8283d", "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ @@ -303,6 +305,16 @@ "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "98294b26", + "metadata": {}, + "outputs": [], + "source": [ + "f(0, np.ones(36)) - f_num(0, np.ones(36))" + ] + }, { "cell_type": "code", "execution_count": null, @@ -403,7 +415,7 @@ { "cell_type": "code", "execution_count": null, - "id": "76ecb077", + "id": "de641ed5", "metadata": {}, "outputs": [], "source": [] diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index 5533bbc..da5f4d6 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -158,7 +158,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables={}, return_symbolic_qgtensor=True)" + "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables={}, return_symbolic_qgtensor=True, language='python')" ] }, { @@ -231,7 +231,7 @@ " for m in range(model_parameters.ndim + 1):\n", " if abs(err[i, j, k, ell, m]) > 1e-15:\n", " if num_t[i, j, k, ell, m] != 0:\n", - " print(i, j, k, ell, m, err[i, j, k, ell, m] / num_t[i, j, k, ell, m])\n" + " print(i, j, k, ell, m, err[i, j, k, ell, m])\n" ] }, { @@ -305,8 +305,17 @@ "\tF[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", "\tF[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", "\tF[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", - "\treturn F\n", - "\n" + "\treturn F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b8fb285", + "metadata": {}, + "outputs": [], + "source": [ + "f(0, np.ones(38)) - f_num(0, np.ones(38))" ] }, { @@ -407,16 +416,6 @@ "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "33661f84", - "metadata": {}, - "outputs": [], - "source": [ - "reference_traj_num[:, -1]" - ] - }, { "cell_type": "code", "execution_count": null, From f98664edadf50c874f8d13f85a443cbefff00aab Mon Sep 17 00:00:00 2001 From: ushham Date: Sun, 20 Aug 2023 18:57:33 +0200 Subject: [PATCH 049/143] Minor bug fixes - tests now passing --- model_test/test_aotensor_sym.py | 4 ++-- model_test/test_base_symbolic.py | 1 - notebooks/Symbolic Output-Linear.ipynb | 2 +- qgs/functions/.cproto | 7 ------- qgs/functions/symbolic_output.py | 12 +++++++++--- qgs/tensors/symbolic_qgtensor.py | 6 +----- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/model_test/test_aotensor_sym.py b/model_test/test_aotensor_sym.py index 87bdddb..c02eaaa 100644 --- a/model_test/test_aotensor_sym.py +++ b/model_test/test_aotensor_sym.py @@ -31,10 +31,10 @@ class TestSymbolicAOTensor(TestBaseSymbolic): filename = 'test_aotensor.ref' def test_sym_against_ref(self): - self.check_lists() + self.check_lists_flt() def test_sym_against_num(self): - self.check_numerical_lists() + self.check_numerical_lists_flt() def symbolic_outputs(self, output_func=None): diff --git a/model_test/test_base_symbolic.py b/model_test/test_base_symbolic.py index 38de7ac..d35c1d4 100644 --- a/model_test/test_base_symbolic.py +++ b/model_test/test_base_symbolic.py @@ -78,7 +78,6 @@ def match_flt(s1, s2, eps=real_eps): v1 = float(s1p[1]) v2 = float(s2p[1]) - print(v1, v2, eps) return abs(v1 - v2) < eps diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index acd036a..d0da83c 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -169,7 +169,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, = create_symbolic_equations(model_parameters, continuation_variables={'eps'}, language='python')" + "funcs, = create_symbolic_equations(model_parameters, continuation_variables={'eps'}, language='auto')" ] }, { diff --git a/qgs/functions/.cproto b/qgs/functions/.cproto index 66b3595..f5e766a 100644 --- a/qgs/functions/.cproto +++ b/qgs/functions/.cproto @@ -2,23 +2,16 @@ #Parameters name # ! PARAMETERS -parnames = {1:'n',2:'epsa',3:'lambda',4:'coup',5:'d',6:'k',7:'kp',8:'Co',9:'Ca',11:'T',12,'theta',14:'t'} - #Variables name # ! VARIABLES -unames = {1:'psi1',2:'psi2',3:'psi3',4:'psi4',5:'psi5',6:'psi6',7:'psi7',8:'psi8',9:'psi9',10:'psi10',11:'theta0',12:'theta1',13:'theta2',14:'theta3',15:'theta4',16:'theta5',17:'theta6',18:'theta7',19:'theta8',20:'theta9',21:'theta10',22:'A1',23:'A2',24:'A3',25:'A4',26:'A5',27:'A6',28:'A7',29:'A8',30:'T0',31:'T1',32:'T2',33:'T3',34:'T4',35:'T5',36:'T6',37:'T7',38:'T8'} - #Dimension of the system # ! DIMENSION -NDIM= 38 - #Problem type (1 for FP, 2 for PO, -2 for time integration) IPS = 1 #Start solution label IRS = 0 #Continuation parameters (in order of use) # ! CONTINUATION ORDER -ICP = ['coup','epsa'] #Number of mesh intervals NTST= 100 #Print and restart every NPR steps (0 to disable) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index a40f487..da77223 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -429,6 +429,7 @@ def create_auto_file(equations, params, free_variables): ''' #TODO: Find out best way to save these files + #TODO: There is some weird double tab spacings in the output, and I am not sure why # User passes the equations, with the variables to leave as variables. # The existing model parameters are used to populate the auto file @@ -501,9 +502,14 @@ def create_auto_file(equations, params, free_variables): elif '! VARIABLES' in ln: auto_config.append('unames = ' + str(_variable_names(params))) + elif '! DIMENSION' in ln: + auto_config.append('NDIM = ' + str(params.ndim)) + elif '! CONTINUATION ORDER' in ln: auto_config.append('ICP = ' + str(list(free_variables))) - + + #TODO: Need to include bounds on continuation parameters + else: auto_config.append(ln.replace('\n', '')) @@ -529,7 +535,8 @@ def _sub_values(params, free_variables): else: raise ValueError("Incorrect type for substitution, needs to be set, list, or dict of strings, not: " + str(type(free_variables))) - + return sub_vals + def _split_equations(eq_dict, f_output, line_len=80): ''' Function to split FORTRAN equaitons to a set length when producing functions @@ -571,7 +578,6 @@ def _variable_names(params): for i in range(offset, num_v[3] + offset): var_list.append('T' + str(i)) - print(var_list) output = dict() for i, v in enumerate(var_list): output[i+1] = v diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index bf41302..70bc865 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -179,8 +179,6 @@ def Cpa(self): def Lpa(self): return self.sym_params['hlambda'] / (self.sym_params['atm_gamma'] * self.sym_params['fo']) - #//TODO: Do we want to keep everthing symbolic? Including the Stefan Bolzmann const? - @property def sbpgo(self): if self.params.gotemperature_params.T0 is None: @@ -743,7 +741,6 @@ def _set_symbolic_tensor(self): if self.params.dynamic_T: if self.params.T4: - #//TODO: Make a proper error message for here raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") else: dims = (ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1) @@ -1263,7 +1260,7 @@ def compute_tensor(self): self._set_tensor(symbolic_dict_dynT) class SymbolicTensorT4(SymbolicTensorLinear): - # TODO: this takes a long time (>1hr) to run. I think we need a better way to run the non-stored z, v, Z, V IPs. + # TODO: this takes a long time (>1hr) to run. I think we need a better way to run the non-stored z, v, Z, V IPs. Maybe do not allow `n` as a continuation parameter for this version? """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. Parameters @@ -1446,7 +1443,6 @@ def compute_tensor(self): """Routine to compute the tensor.""" # gathering if not(self.params.T4): - #//TODO: Make a proper error message for here raise ValueError("Parameters are not set for T4 version") symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) From f48ff3e153a83791d2f7cfbefa6b6bc1dd50bc02 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 24 Aug 2023 16:53:40 +0200 Subject: [PATCH 050/143] Change to subs to work with Sympy==1.12 --- qgs/functions/symbolic_output.py | 12 ++++++++---- qgs/inner_products/symbolic.py | 17 +++++++---------- qgs/tensors/symbolic_qgtensor.py | 4 +++- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index da77223..fec5174 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -521,13 +521,17 @@ def _sub_values(params, free_variables): # Substitute variables if isinstance(free_variables, (set, list, dict)): # make a dictionary of variables to substitute from parameters + sub_vals = dict() for key in params.symbol_to_value.keys(): - if len(free_variables) == 0: - sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] - else: - if key not in free_variables: + + # Sympy 1.12 cannot have subs contain None + if params.symbol_to_value[key][1] is not None: + if len(free_variables) == 0: sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] + else: + if key not in free_variables: + sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] elif free_variables is None: sub_vals = None diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 74294cb..5c47144 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -36,9 +36,6 @@ # TODO: - Add warnings if trying to connect analytic and symbolic inner products together -# _n = sy.Symbol('n', positive=True) - - class AtmosphericSymbolicInnerProducts(AtmosphericInnerProducts): """Class which contains all the atmospheric inner products coefficients needed for the tendencies tensor :class:`~.tensors.qgtensor.QgsTensor` computation, computed with analytic formula. @@ -177,13 +174,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.mk_subs = make_substitution # _parallel_compute = _symbolic_compute if self.mk_subs: - self.subs = [('n', self.n)] + self.subs = [(params.symbolic_params['n'], self.n)] else: self.subs = None else: self.mk_subs = True # _parallel_compute = _parallel_compute - self.subs = [('n', self.n)] + self.subs = [(params.symbolic_params['n'], self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() @@ -249,7 +246,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): subs = self.subs + self.atmospheric_basis.substitutions + self.oceanic_basis.substitutions else: subs = self.subs - + print(subs) noc = len(ocean_basis) if self.return_symbolic: self._gh = None @@ -820,13 +817,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.mk_subs = make_substitution # _parallel_compute = _symbolic_compute if self.mk_subs: - self.subs = [('n', self.n)] + self.subs = [(params.symbolic_params['n'], self.n)] else: self.subs = None else: self.mk_subs = True # _parallel_compute = _parallel_compute - self.subs = [('n', self.n)] + self.subs = [(params.symbolic_params['n'], self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() @@ -1305,13 +1302,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.mk_subs = make_substitution # _parallel_compute = _symbolic_compute if self.mk_subs: - self.subs = [('n', self.n)] + self.subs = [(params.symbolic_params['n'], self.n)] else: self.subs = None else: self.mk_subs = True # _parallel_compute = _parallel_compute - self.subs = [('n', self.n)] + self.subs = [(params.symbolic_params['n'], self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 70bc865..2104dc3 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -832,7 +832,9 @@ def sub_tensor(self, tensor=None, dict_opp=True, remain_variables=dict()): for key in self.sym_params.keys(): if key not in remain_variables: try: - symbol_to_number_map.append(self.params.symbol_to_value[key]) + # Sympy 1.12 cannot have subs contain None + if self.params.symbol_to_value[key][1] is not None: + symbol_to_number_map.append(self.params.symbol_to_value[key]) except: variables_not_found.append(key) From 9a5e5c41026728c6f4208bed53784db04759369e Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 24 Aug 2023 17:51:44 +0200 Subject: [PATCH 051/143] Bugs in substitutions fixed --- model_test/test_aotensor_sym.py | 51 --------------- notebooks/Symbolic Output-Linear.ipynb | 91 +++++++++++++------------- qgs/inner_products/symbolic.py | 2 +- 3 files changed, 46 insertions(+), 98 deletions(-) diff --git a/model_test/test_aotensor_sym.py b/model_test/test_aotensor_sym.py index c02eaaa..9acfa23 100644 --- a/model_test/test_aotensor_sym.py +++ b/model_test/test_aotensor_sym.py @@ -98,54 +98,3 @@ def _ip_string_format(func, symbol, indices, value): if __name__ == "__main__": unittest.main() - -# def test_tensor_numerically(self, tensor=None, dict_opp=True, tol=1e-10): -# """ -# Uses sympy substitution to convert the symbolic tensor, or symbolic dictionary, to a numerical one. -# This is then compared to the tensor calculated by the qgs.tensor.symbolic module. - -# """ -# ndim = self.params.ndim - -# if self.params.dynamic_T: -# if self.params.T4: - -# raise ValueError("Symbolic tensor output not configured for T4 version, use Dynamic T version") -# else: -# dims = (ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1) -# else: -# dims = (ndim + 1, ndim + 1, ndim + 1) - -# _, _, numerical_tensor = create_tendencies(self.params, return_qgtensor=True) - -# if tensor is None: -# if dict_opp: -# tensor = self.tensor_dic -# else: -# tensor = self.tensor - -# subbed_ten = self.subs_tensor(tensor) -# if isinstance(subbed_ten, dict): -# coords = np.array([list(k) for k in subbed_ten.keys()]).T -# data = np.array(list(subbed_ten.values()), dtype=float) -# subbed_tensor_sp = sp.COO(coords, data, shape=dims) -# else: -# subbed_ten = np.array(subbed_ten) -# subbed_tensor_np = np.array(subbed_ten).astype(np.float64) -# subbed_tensor_sp = sp.COO.from_numpy(subbed_tensor_np) - -# diff_arr = subbed_tensor_sp.todense() - numerical_tensor.tensor.todense() - - -# total_error = np.sum(np.abs(diff_arr)) -# max_error = np.max(np.abs(diff_arr)) - -# if max_error > tol: -# self.print_tensor(diff_arr, tol=tol) - -# raise ValueError("Symbolic tensor and numerical tensor do not match at the above coordinates, with a total error of: " + str(total_error)) - -# elif total_error > tol: -# warnings.warn("Symbolic tensor and numerical tensor have a combined error of: " + str(total_error)) -# else: -# print("Tensor passes numerical test with a combined error of less than: " + str(tol)) \ No newline at end of file diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index d0da83c..b26d313 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -58,7 +58,8 @@ "metadata": {}, "outputs": [], "source": [ - "model_parameters = QgParams(dynamic_T=False)" + "model_parameters = QgParams({'rr': 287.e0, 'sb': 5.6e-8}, dynamic_T=False)\n", + "model_parameters.set_params({'kd': 0.04, 'kdp': 0.04, 'n': 1.5})" ] }, { @@ -106,8 +107,8 @@ }, "outputs": [], "source": [ - "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True) \n", - "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters, stored=False, return_symbolic=True)" + "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True) \n", + "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)" ] }, { @@ -148,7 +149,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations" + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations, _sub_values" ] }, { @@ -169,7 +170,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, = create_symbolic_equations(model_parameters, continuation_variables={'eps'}, language='auto')" + "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables={'eps'}, language='python', return_symbolic_qgtensor=True)" ] }, { @@ -177,7 +178,7 @@ "execution_count": null, "id": "5fd8283d", "metadata": { - "scrolled": false + "scrolled": true }, "outputs": [], "source": [ @@ -212,46 +213,44 @@ "@njit\n", "def f(t, U):\n", "\t#Tendency function of the qgs model\n", - "\tU_out = np.empty_like(U)\n", - "\tn = 1.3\n", - "\tepsilon = 0.76\n", - "\tU_out[0] = -0.05*U[0] + 0.05*U[10] + 0.00716448960313446*U[21]*(1.33333333333333*n**2 + 21.3333333333333) + 0.00716448960313446*U[23]*(0.533333333333333*n**2 + 34.1333333333333)\n", - "\tU_out[1] = U[0]*U[2]*(-1.20042175487614*n + 1.20042175487614*n/(n**2 + 1.0)) + U[10]*U[12]*(-1.20042175487614*n + 1.20042175487614*n/(n**2 + 1.0)) + 0.05*U[11] + U[13]*U[15]*(-1.92067480780183*n*(n**2 + 4.0)/(n**2 + 1.0) + 7.68269923120731*n/(n**2 + 1.0)) + U[14]*U[17]*(1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 1.0)) + U[15]*U[16]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0)) - 0.05*U[1] - 0.0159154943091895*U[20]*(0.333333333333333*n**2 + 1.33333333333333)/(n**2 + 1.0) + 0.249850774084608*U[2]*n/(n**2 + 1.0) + U[3]*U[5]*(-1.92067480780183*n*(n**2 + 4.0)/(n**2 + 1.0) + 7.68269923120731*n/(n**2 + 1.0)) + U[4]*U[7]*(1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 1.0)) + U[5]*U[6]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0))\n", - "\tU_out[2] = U[0]*U[1]*(1.20042175487614*n - 1.20042175487614*n/(n**2 + 1.0)) + U[10]*U[11]*(1.20042175487614*n - 1.20042175487614*n/(n**2 + 1.0)) + 0.05*U[12] + U[13]*U[14]*(1.92067480780183*n*(n**2 + 4.0)/(n**2 + 1.0) - 7.68269923120731*n/(n**2 + 1.0)) + U[14]*U[16]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0)) + U[15]*U[17]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0)) - 0.249850774084608*U[1]*n/(n**2 + 1.0) - 0.05*U[24]*(-1.0*n**2 - 1.0)/(n**2 + 1.0) - 0.05*U[2] + U[3]*U[4]*(1.92067480780183*n*(n**2 + 4.0)/(n**2 + 1.0) - 7.68269923120731*n/(n**2 + 1.0)) + U[4]*U[6]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0)) + U[5]*U[7]*(-1.5*n*(n**2 + 4.0)/(n**2 + 1.0) + (6.0*n**3 + 1.5*n)/(n**2 + 1.0))\n", - "\tU_out[3] = U[11]*U[15]*(-0.480168701950457*n*(n**2 + 1.0) + 0.480168701950457*n*(n**2 + 4.0)) + U[12]*U[14]*(0.480168701950457*n*(n**2 + 1.0) - 0.480168701950457*n*(n**2 + 4.0)) + 0.05*U[13] + U[16]*U[19]*(3.84134961560365*n*(n**2 + 1.0) - 0.960337403900913*n*(4.0*n**2 + 1.0)) + U[17]*U[18]*(-3.84134961560365*n*(n**2 + 1.0) + 0.960337403900913*n*(4.0*n**2 + 1.0)) + U[1]*U[5]*(-0.480168701950457*n*(n**2 + 1.0) + 0.480168701950457*n*(n**2 + 4.0)) - 0.00179112240078361*U[20]*(0.666666666666667*n**2 + 2.66666666666667) + 0.00179112240078361*U[22]*(1.2*n**2 + 43.2) + U[2]*U[4]*(0.480168701950457*n*(n**2 + 1.0) - 0.480168701950457*n*(n**2 + 4.0)) - 0.05*U[3] + U[6]*U[9]*(3.84134961560365*n*(n**2 + 1.0) - 0.960337403900913*n*(4.0*n**2 + 1.0)) + U[7]*U[8]*(-3.84134961560365*n*(n**2 + 1.0) + 0.960337403900913*n*(4.0*n**2 + 1.0))\n", - "\tU_out[4] = U[0]*U[5]*(-0.960337403900913*n + 0.960337403900913*n/(n**2 + 4.0)) + U[10]*U[15]*(-0.960337403900913*n + 0.960337403900913*n/(n**2 + 4.0)) + U[11]*U[17]*(-1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (6.0*n**3 + 1.5*n)/(n**2 + 4.0)) + U[12]*U[13]*(-1.92067480780183*n*(n**2 + 1.0)/(n**2 + 4.0) + 7.68269923120731*n/(n**2 + 4.0)) + U[12]*U[16]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) + 0.05*U[14] + U[1]*U[7]*(-1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (6.0*n**3 + 1.5*n)/(n**2 + 4.0)) - 0.0159154943091895*U[21]*(0.333333333333333*n**2 + 5.33333333333333)/(n**2 + 4.0) + U[2]*U[3]*(-1.92067480780183*n*(n**2 + 1.0)/(n**2 + 4.0) + 7.68269923120731*n/(n**2 + 4.0)) + U[2]*U[6]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) - 0.05*U[4] + 0.249850774084608*U[5]*n/(n**2 + 4.0)\n", - "\tU_out[5] = U[0]*U[4]*(0.960337403900913*n - 0.960337403900913*n/(n**2 + 4.0)) + U[10]*U[14]*(0.960337403900913*n - 0.960337403900913*n/(n**2 + 4.0)) + U[11]*U[13]*(1.92067480780183*n*(n**2 + 1.0)/(n**2 + 4.0) - 7.68269923120731*n/(n**2 + 4.0)) + U[11]*U[16]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) + U[12]*U[17]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) + 0.05*U[15] + U[1]*U[3]*(1.92067480780183*n*(n**2 + 1.0)/(n**2 + 4.0) - 7.68269923120731*n/(n**2 + 4.0)) + U[1]*U[6]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) - 0.05*U[25]*(-1.0*n**2 - 4.0)/(n**2 + 4.0) + U[2]*U[7]*(1.5*n*(n**2 + 1.0)/(n**2 + 4.0) + (-6.0*n**3 - 1.5*n)/(n**2 + 4.0)) - 0.249850774084608*U[4]*n/(n**2 + 4.0) - 0.05*U[5]\n", - "\tU_out[6] = U[0]*U[7]*(-2.40084350975228*n + 2.40084350975228*n/(4.0*n**2 + 1.0)) + U[10]*U[17]*(-2.40084350975228*n + 2.40084350975228*n/(4.0*n**2 + 1.0)) + U[11]*U[15]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[12]*U[14]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[13]*U[19]*(-15.3653984624146*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 15.3653984624146*n/(4.0*n**2 + 1.0)) + 0.05*U[16] + U[1]*U[5]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) - 0.0159154943091895*U[20]*(0.0666666666666667*n**2 + 0.266666666666667)/(4.0*n**2 + 1.0) + U[2]*U[4]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[3]*U[9]*(-15.3653984624146*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 15.3653984624146*n/(4.0*n**2 + 1.0)) - 0.05*U[6] + 0.499701548169216*U[7]*n/(4.0*n**2 + 1.0)\n", - "\tU_out[7] = U[0]*U[6]*(2.40084350975228*n - 2.40084350975228*n/(4.0*n**2 + 1.0)) + U[10]*U[16]*(2.40084350975228*n - 2.40084350975228*n/(4.0*n**2 + 1.0)) + U[11]*U[14]*(1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) - 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[12]*U[15]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[13]*U[18]*(15.3653984624146*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) - 15.3653984624146*n/(4.0*n**2 + 1.0)) + 0.05*U[17] + U[1]*U[4]*(1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) - 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[2]*U[5]*(-1.5*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) + 1.5*n*(n**2 + 4.0)/(4.0*n**2 + 1.0)) + U[3]*U[8]*(15.3653984624146*n*(n**2 + 1.0)/(4.0*n**2 + 1.0) - 15.3653984624146*n/(4.0*n**2 + 1.0)) - 0.499701548169216*U[6]*n/(4.0*n**2 + 1.0) - 0.05*U[7]\n", - "\tU_out[8] = U[0]*U[9]*(-7.68269923120731*n*(n**2 + 1.0)/(4.0*n**2 + 4.0) + 1.92067480780183*n/(4.0*n**2 + 4.0)) + U[10]*U[19]*(-7.68269923120731*n*(n**2 + 1.0)/(4.0*n**2 + 4.0) + 1.92067480780183*n/(4.0*n**2 + 4.0)) + U[13]*U[17]*(-3.84134961560365*n*(4.0*n**2 + 1.0)/(4.0*n**2 + 4.0) + 15.3653984624146*n/(4.0*n**2 + 4.0)) + 0.05*U[18] - 0.0159154943091895*U[21]*(0.0666666666666667*n**2 + 1.06666666666667)/(4.0*n**2 + 4.0) + U[3]*U[7]*(-3.84134961560365*n*(4.0*n**2 + 1.0)/(4.0*n**2 + 4.0) + 15.3653984624146*n/(4.0*n**2 + 4.0)) - 0.05*U[8] + 0.499701548169216*U[9]*n/(4.0*n**2 + 4.0)\n", - "\tU_out[9] = U[0]*U[8]*(7.68269923120731*n*(n**2 + 1.0)/(4.0*n**2 + 4.0) - 1.92067480780183*n/(4.0*n**2 + 4.0)) + U[10]*U[18]*(7.68269923120731*n*(n**2 + 1.0)/(4.0*n**2 + 4.0) - 1.92067480780183*n/(4.0*n**2 + 4.0)) + U[13]*U[16]*(3.84134961560365*n*(4.0*n**2 + 1.0)/(4.0*n**2 + 4.0) - 15.3653984624146*n/(4.0*n**2 + 4.0)) + 0.05*U[19] + U[3]*U[6]*(3.84134961560365*n*(4.0*n**2 + 1.0)/(4.0*n**2 + 4.0) - 15.3653984624146*n/(4.0*n**2 + 4.0)) - 0.499701548169216*U[8]*n/(4.0*n**2 + 4.0) - 0.05*U[9]\n", - "\tU_out[10] = 0.00454545454545455*U[0] + U[10]*(-0.00786487737843552*epsilon - 0.0239816772374912) - 1.09129250443286*U[11]*U[2]*n + 1.09129250443286*U[12]*U[1]*n - 0.873034003546285*U[14]*U[5]*n + 0.873034003546285*U[15]*U[4]*n - 2.18258500886571*U[16]*U[7]*n + 2.18258500886571*U[17]*U[6]*n - 1.74606800709257*U[18]*U[9]*n + 1.74606800709257*U[19]*U[8]*n - 0.000651317236648587*U[21]*(1.33333333333333*n**2 + 21.3333333333333) - 0.000651317236648587*U[23]*(0.533333333333333*n**2 + 34.1333333333333) + U[29]*(0.00176721436265574*epsilon + 0.00673196110231097) + U[31]*(0.000706885745062296*epsilon + 0.00269278444092439) + 0.000468670500616653\n", - "\tU_out[11] = U[0]*U[12]*(-0.240084350975228*n*(n**2 + 1.0)/(0.2*n**2 + 2.2) - 2.16075915877705*n/(0.2*n**2 + 2.2)) + U[10]*U[2]*(-0.240084350975228*n*(n**2 + 1.0)/(0.2*n**2 + 2.2) + 2.64092786072751*n/(0.2*n**2 + 2.2)) + U[11]*(-0.0173027302325581*epsilon/(0.2*n**2 + 2.2) + 0.014*(-n**2 - 1.0)/(0.2*n**2 + 2.2) - 0.0387596899224806/(0.2*n**2 + 2.2)) + 0.0499701548169216*U[12]*n/(0.2*n**2 + 2.2) + U[13]*U[5]*(-0.384134961560365*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 5.37788946184511*n/(0.2*n**2 + 2.2)) + U[14]*U[7]*(0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 3.0*n/(0.2*n**2 + 2.2) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.2)) + U[15]*U[3]*(-0.384134961560365*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 2.30480976936219*n/(0.2*n**2 + 2.2)) + U[15]*U[6]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[16]*U[5]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[17]*U[4]*(0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 3.0*n/(0.2*n**2 + 2.2) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.2)) - 0.01*U[1]*(-n**2 - 1.0)/(0.2*n**2 + 2.2) + 0.00318309886183791*U[20]*(0.333333333333333*n**2 + 1.33333333333333)/(0.2*n**2 + 2.2) + U[28]*(-0.00215916979847569*epsilon/(0.2*n**2 + 2.2) - 0.00822506165849588/(0.2*n**2 + 2.2))\n", - "\tU_out[12] = U[0]*U[11]*(0.240084350975228*n*(n**2 + 1.0)/(0.2*n**2 + 2.2) + 2.16075915877705*n/(0.2*n**2 + 2.2)) + U[10]*U[1]*(0.240084350975228*n*(n**2 + 1.0)/(0.2*n**2 + 2.2) - 2.64092786072751*n/(0.2*n**2 + 2.2)) - 0.0499701548169216*U[11]*n/(0.2*n**2 + 2.2) + U[12]*(-0.0173027302325581*epsilon/(0.2*n**2 + 2.2) + 0.014*(-n**2 - 1.0)/(0.2*n**2 + 2.2) - 0.0387596899224806/(0.2*n**2 + 2.2)) + U[13]*U[4]*(0.384134961560365*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 5.37788946184511*n/(0.2*n**2 + 2.2)) + U[14]*U[3]*(0.384134961560365*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 2.30480976936219*n/(0.2*n**2 + 2.2)) + U[14]*U[6]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[15]*U[7]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) - 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[16]*U[4]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + U[17]*U[5]*(-0.3*n*(n**2 + 4.0)/(0.2*n**2 + 2.2) + 3.0*n/(0.2*n**2 + 2.2) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.2)) + 0.01*U[24]*(-1.0*n**2 - 1.0)/(0.2*n**2 + 2.2) - 0.01*U[2]*(-n**2 - 1.0)/(0.2*n**2 + 2.2) + U[32]*(0.00508742398255814*epsilon/(0.2*n**2 + 2.2) + 0.0193798449612403/(0.2*n**2 + 2.2))\n", - "\tU_out[13] = U[11]*U[5]*(-0.13719105770013*n*(n**2 + 1.0) + 0.13719105770013*n*(n**2 + 4.0) - 1.3719105770013*n) + U[12]*U[4]*(0.13719105770013*n*(n**2 + 1.0) - 0.13719105770013*n*(n**2 + 4.0) + 1.3719105770013*n) + U[13]*(-0.00617954651162791*epsilon - 0.0338427464008859) + U[14]*U[2]*(0.13719105770013*n*(n**2 + 1.0) - 0.13719105770013*n*(n**2 + 4.0) - 1.3719105770013*n) + U[15]*U[1]*(-0.13719105770013*n*(n**2 + 1.0) + 0.13719105770013*n*(n**2 + 4.0) + 1.3719105770013*n) + U[16]*U[9]*(1.09752846160104*n*(n**2 + 1.0) - 0.274382115400261*n*(4.0*n**2 + 1.0) - 2.74382115400261*n) + U[17]*U[8]*(-1.09752846160104*n*(n**2 + 1.0) + 0.274382115400261*n*(4.0*n**2 + 1.0) + 2.74382115400261*n) + U[18]*U[7]*(-1.09752846160104*n*(n**2 + 1.0) + 0.274382115400261*n*(4.0*n**2 + 1.0) - 2.74382115400261*n) + U[19]*U[6]*(1.09752846160104*n*(n**2 + 1.0) - 0.274382115400261*n*(4.0*n**2 + 1.0) + 2.74382115400261*n) + 0.000511749257366747*U[20]*(0.666666666666667*n**2 + 2.66666666666667) - 0.000511749257366747*U[22]*(1.2*n**2 + 43.2) + U[28]*(-0.000694262785329041*epsilon - 0.00264469900447931) + U[30]*(0.00124967301359227*epsilon + 0.00476045820806276) + 0.0142857142857143*U[3]\n", - "\tU_out[14] = U[0]*U[15]*(-0.192067480780183*n*(n**2 + 4.0)/(0.2*n**2 + 2.8) - 1.72860732702164*n/(0.2*n**2 + 2.8)) + U[10]*U[5]*(-0.192067480780183*n*(n**2 + 4.0)/(0.2*n**2 + 2.8) + 2.11274228858201*n/(0.2*n**2 + 2.8)) + U[11]*U[7]*(-0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 3.0*n/(0.2*n**2 + 2.8) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.8)) + U[12]*U[3]*(-0.384134961560365*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 2.30480976936219*n/(0.2*n**2 + 2.8)) + U[12]*U[6]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[13]*U[2]*(-0.384134961560365*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 5.37788946184511*n/(0.2*n**2 + 2.8)) + U[14]*(-0.0173027302325581*epsilon/(0.2*n**2 + 2.8) + 0.014*(-n**2 - 4.0)/(0.2*n**2 + 2.8) - 0.0387596899224806/(0.2*n**2 + 2.8)) + 0.0499701548169216*U[15]*n/(0.2*n**2 + 2.8) + U[16]*U[2]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[17]*U[1]*(-0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 3.0*n/(0.2*n**2 + 2.8) + 0.2*(6.0*n**3 + 1.5*n)/(0.2*n**2 + 2.8)) + 0.00318309886183791*U[21]*(0.333333333333333*n**2 + 5.33333333333333)/(0.2*n**2 + 2.8) + U[29]*(-0.00215916979847569*epsilon/(0.2*n**2 + 2.8) - 0.00822506165849588/(0.2*n**2 + 2.8)) - 0.01*U[4]*(-n**2 - 4.0)/(0.2*n**2 + 2.8)\n", - "\tU_out[15] = U[0]*U[14]*(0.192067480780183*n*(n**2 + 4.0)/(0.2*n**2 + 2.8) + 1.72860732702164*n/(0.2*n**2 + 2.8)) + U[10]*U[4]*(0.192067480780183*n*(n**2 + 4.0)/(0.2*n**2 + 2.8) - 2.11274228858201*n/(0.2*n**2 + 2.8)) + U[11]*U[3]*(0.384134961560365*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 2.30480976936219*n/(0.2*n**2 + 2.8)) + U[11]*U[6]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[12]*U[7]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) + 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[13]*U[1]*(0.384134961560365*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 5.37788946184511*n/(0.2*n**2 + 2.8)) - 0.0499701548169216*U[14]*n/(0.2*n**2 + 2.8) + U[15]*(-0.0173027302325581*epsilon/(0.2*n**2 + 2.8) + 0.014*(-n**2 - 4.0)/(0.2*n**2 + 2.8) - 0.0387596899224806/(0.2*n**2 + 2.8)) + U[16]*U[1]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + U[17]*U[2]*(0.3*n*(n**2 + 1.0)/(0.2*n**2 + 2.8) - 3.0*n/(0.2*n**2 + 2.8) + 0.2*(-6.0*n**3 - 1.5*n)/(0.2*n**2 + 2.8)) + 0.01*U[25]*(-1.0*n**2 - 4.0)/(0.2*n**2 + 2.8) + U[33]*(0.00508742398255814*epsilon/(0.2*n**2 + 2.8) + 0.0193798449612403/(0.2*n**2 + 2.8)) - 0.01*U[5]*(-n**2 - 4.0)/(0.2*n**2 + 2.8)\n", - "\tU_out[16] = U[0]*U[17]*(-0.480168701950457*n*(4.0*n**2 + 1.0)/(0.8*n**2 + 2.2) - 4.32151831755411*n/(0.8*n**2 + 2.2)) + U[10]*U[7]*(-0.480168701950457*n*(4.0*n**2 + 1.0)/(0.8*n**2 + 2.2) + 5.28185572145502*n/(0.8*n**2 + 2.2)) + U[11]*U[5]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) - 3.0*n/(0.8*n**2 + 2.2)) + U[12]*U[4]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) - 3.0*n/(0.8*n**2 + 2.2)) + U[13]*U[9]*(-3.07307969248292*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 10.7557789236902*n/(0.8*n**2 + 2.2)) + U[14]*U[2]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) + 3.0*n/(0.8*n**2 + 2.2)) + U[15]*U[1]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) + 3.0*n/(0.8*n**2 + 2.2)) + U[16]*(-0.0173027302325581*epsilon/(0.8*n**2 + 2.2) + 0.014*(-4.0*n**2 - 1.0)/(0.8*n**2 + 2.2) - 0.0387596899224806/(0.8*n**2 + 2.2)) + 0.0999403096338432*U[17]*n/(0.8*n**2 + 2.2) + U[19]*U[3]*(-3.07307969248292*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) - 4.60961953872438*n/(0.8*n**2 + 2.2)) + 0.00318309886183791*U[20]*(0.0666666666666667*n**2 + 0.266666666666667)/(0.8*n**2 + 2.2) + U[28]*(-0.000431833959695138*epsilon/(0.8*n**2 + 2.2) - 0.00164501233169918/(0.8*n**2 + 2.2)) - 0.01*U[6]*(-4.0*n**2 - 1.0)/(0.8*n**2 + 2.2)\n", - "\tU_out[17] = U[0]*U[16]*(0.480168701950457*n*(4.0*n**2 + 1.0)/(0.8*n**2 + 2.2) + 4.32151831755411*n/(0.8*n**2 + 2.2)) + U[10]*U[6]*(0.480168701950457*n*(4.0*n**2 + 1.0)/(0.8*n**2 + 2.2) - 5.28185572145502*n/(0.8*n**2 + 2.2)) + U[11]*U[4]*(0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) - 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) + 3.0*n/(0.8*n**2 + 2.2)) + U[12]*U[5]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) - 3.0*n/(0.8*n**2 + 2.2)) + U[13]*U[8]*(3.07307969248292*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) - 10.7557789236902*n/(0.8*n**2 + 2.2)) + U[14]*U[1]*(0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) - 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) - 3.0*n/(0.8*n**2 + 2.2)) + U[15]*U[2]*(-0.3*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 0.3*n*(n**2 + 4.0)/(0.8*n**2 + 2.2) + 3.0*n/(0.8*n**2 + 2.2)) - 0.0999403096338432*U[16]*n/(0.8*n**2 + 2.2) + U[17]*(-0.0173027302325581*epsilon/(0.8*n**2 + 2.2) + 0.014*(-4.0*n**2 - 1.0)/(0.8*n**2 + 2.2) - 0.0387596899224806/(0.8*n**2 + 2.2)) + U[18]*U[3]*(3.07307969248292*n*(n**2 + 1.0)/(0.8*n**2 + 2.2) + 4.60961953872438*n/(0.8*n**2 + 2.2)) - 0.01*U[7]*(-4.0*n**2 - 1.0)/(0.8*n**2 + 2.2)\n", - "\tU_out[18] = U[0]*U[19]*(-0.768269923120731*n*(n**2 + 1.0)/(0.4*n**2 + 1.4) - 1.72860732702164*n/(0.4*n**2 + 1.4)) + U[10]*U[9]*(-0.768269923120731*n*(n**2 + 1.0)/(0.4*n**2 + 1.4) + 2.11274228858201*n/(0.4*n**2 + 1.4)) + U[13]*U[7]*(-0.384134961560365*n*(4.0*n**2 + 1.0)/(0.4*n**2 + 1.4) + 5.37788946184511*n/(0.4*n**2 + 1.4)) + U[17]*U[3]*(-0.384134961560365*n*(4.0*n**2 + 1.0)/(0.4*n**2 + 1.4) - 2.30480976936219*n/(0.4*n**2 + 1.4)) + U[18]*(-0.00865136511627907*epsilon/(0.4*n**2 + 1.4) + 0.007*(-4.0*n**2 - 4.0)/(0.4*n**2 + 1.4) - 0.0193798449612403/(0.4*n**2 + 1.4)) + 0.0499701548169216*U[19]*n/(0.4*n**2 + 1.4) + 0.00159154943091895*U[21]*(0.0666666666666667*n**2 + 1.06666666666667)/(0.4*n**2 + 1.4) + U[29]*(-0.000215916979847569*epsilon/(0.4*n**2 + 1.4) - 0.000822506165849588/(0.4*n**2 + 1.4)) - 0.005*U[8]*(-4.0*n**2 - 4.0)/(0.4*n**2 + 1.4)\n", - "\tU_out[19] = U[0]*U[18]*(0.768269923120731*n*(n**2 + 1.0)/(0.4*n**2 + 1.4) + 1.72860732702164*n/(0.4*n**2 + 1.4)) + U[10]*U[8]*(0.768269923120731*n*(n**2 + 1.0)/(0.4*n**2 + 1.4) - 2.11274228858201*n/(0.4*n**2 + 1.4)) + U[13]*U[6]*(0.384134961560365*n*(4.0*n**2 + 1.0)/(0.4*n**2 + 1.4) - 5.37788946184511*n/(0.4*n**2 + 1.4)) + U[16]*U[3]*(0.384134961560365*n*(4.0*n**2 + 1.0)/(0.4*n**2 + 1.4) + 2.30480976936219*n/(0.4*n**2 + 1.4)) - 0.0499701548169216*U[18]*n/(0.4*n**2 + 1.4) + U[19]*(-0.00865136511627907*epsilon/(0.4*n**2 + 1.4) + 0.007*(-4.0*n**2 - 4.0)/(0.4*n**2 + 1.4) - 0.0193798449612403/(0.4*n**2 + 1.4)) - 0.005*U[9]*(-4.0*n**2 - 4.0)/(0.4*n**2 + 1.4)\n", - "\tU_out[20] = 0.000637442278533431*U[11]*(n**2 + 1.0)/(3.875*n**2 + 26992.8730718743) + 0.00229559873588804*U[13]/(3.875*n**2 + 26992.8730718743) + 0.000478081708900073*U[16]*(1.06666666666667*n**2 + 0.266666666666667)/(3.875*n**2 + 26992.8730718743) - 0.000637442278533431*U[1]*(n**2 + 1.0)/(3.875*n**2 + 26992.8730718743) + 0.00300387596899225*U[20]*(-0.25*n**2 - 1.0)/(3.875*n**2 + 26992.8730718743) + U[21]*U[24]*(-15.5*n*(0.1875*n**2 + 3.0)/(3.875*n**2 + 26992.8730718743) + 11.625*n*(n**2 + 1.0)/(3.875*n**2 + 26992.8730718743)) + U[21]*U[26]*(15.5*n*(0.0625*n**2 + 1.0)/(3.875*n**2 + 26992.8730718743) - 15.5*n*(0.25*n**2 + 2.25)/(3.875*n**2 + 26992.8730718743)) + U[22]*U[25]*(-15.5*n*(0.25*n**2 + 9.0)/(3.875*n**2 + 26992.8730718743) + 15.5*n*(1.0*n**2 + 4.0)/(3.875*n**2 + 26992.8730718743)) + U[22]*U[27]*(15.5*n*(0.125*n**2 + 4.5)/(3.875*n**2 + 26992.8730718743) - 15.5*n*(0.5*n**2 + 8.0)/(3.875*n**2 + 26992.8730718743)) + U[23]*U[26]*(-15.5*n*(0.3125*n**2 + 20.0)/(3.875*n**2 + 26992.8730718743) + 15.5*n*(1.25*n**2 + 11.25)/(3.875*n**2 + 26992.8730718743)) - 1.64361941021061*U[24]*n/(3.875*n**2 + 26992.8730718743) - 0.00229559873588804*U[3]/(3.875*n**2 + 26992.8730718743) - 0.000478081708900073*U[6]*(1.06666666666667*n**2 + 0.266666666666667)/(3.875*n**2 + 26992.8730718743)\n", - "\tU_out[21] = 0.00114779936794402*U[0]/(3.875*n**2 + 27039.3730718743) - 0.00114779936794402*U[10]/(3.875*n**2 + 27039.3730718743) + 0.000478081708900073*U[14]*(1.33333333333333*n**2 + 5.33333333333333)/(3.875*n**2 + 27039.3730718743) + 0.000509953822826745*U[18]*(n**2 + 1.0)/(3.875*n**2 + 27039.3730718743) + U[20]*U[24]*(15.5*n*(0.1875*n**2 + 0.75)/(3.875*n**2 + 27039.3730718743) - 11.625*n*(n**2 + 1.0)/(3.875*n**2 + 27039.3730718743)) + U[20]*U[26]*(-15.5*n*(0.0625*n**2 + 0.25)/(3.875*n**2 + 27039.3730718743) + 15.5*n*(0.25*n**2 + 2.25)/(3.875*n**2 + 27039.3730718743)) + 0.00300387596899225*U[21]*(-0.25*n**2 - 4.0)/(3.875*n**2 + 27039.3730718743) + U[22]*U[24]*(-15.5*n*(0.3125*n**2 + 11.25)/(3.875*n**2 + 27039.3730718743) + 19.375*n*(n**2 + 1.0)/(3.875*n**2 + 27039.3730718743)) + U[23]*U[25]*(-15.5*n*(0.375*n**2 + 24.0)/(3.875*n**2 + 27039.3730718743) + 15.5*n*(1.5*n**2 + 6.0)/(3.875*n**2 + 27039.3730718743)) - 1.64361941021061*U[25]*n/(3.875*n**2 + 27039.3730718743) - 0.000478081708900073*U[4]*(1.33333333333333*n**2 + 5.33333333333333)/(3.875*n**2 + 27039.3730718743) - 0.000509953822826745*U[8]*(n**2 + 1.0)/(3.875*n**2 + 27039.3730718743)\n", - "\tU_out[22] = -0.00413207772459848*U[13]/(3.875*n**2 + 27116.8730718743) + U[20]*U[25]*(15.5*n*(0.25*n**2 + 1.0)/(3.875*n**2 + 27116.8730718743) - 15.5*n*(1.0*n**2 + 4.0)/(3.875*n**2 + 27116.8730718743)) + U[20]*U[27]*(-15.5*n*(0.125*n**2 + 0.5)/(3.875*n**2 + 27116.8730718743) + 15.5*n*(0.5*n**2 + 8.0)/(3.875*n**2 + 27116.8730718743)) + U[21]*U[24]*(15.5*n*(0.3125*n**2 + 5.0)/(3.875*n**2 + 27116.8730718743) - 19.375*n*(n**2 + 1.0)/(3.875*n**2 + 27116.8730718743)) + 0.00300387596899225*U[22]*(-0.25*n**2 - 9.0)/(3.875*n**2 + 27116.8730718743) + U[23]*U[24]*(-15.5*n*(0.4375*n**2 + 28.0)/(3.875*n**2 + 27116.8730718743) + 27.125*n*(n**2 + 1.0)/(3.875*n**2 + 27116.8730718743)) - 1.64361941021061*U[26]*n/(3.875*n**2 + 27116.8730718743) + 0.00413207772459848*U[3]/(3.875*n**2 + 27116.8730718743)\n", - "\tU_out[23] = 0.000459119747177608*U[0]/(3.875*n**2 + 27225.3730718743) - 0.000459119747177608*U[10]/(3.875*n**2 + 27225.3730718743) + U[20]*U[26]*(15.5*n*(0.3125*n**2 + 1.25)/(3.875*n**2 + 27225.3730718743) - 15.5*n*(1.25*n**2 + 11.25)/(3.875*n**2 + 27225.3730718743)) + U[21]*U[25]*(15.5*n*(0.375*n**2 + 6.0)/(3.875*n**2 + 27225.3730718743) - 15.5*n*(1.5*n**2 + 6.0)/(3.875*n**2 + 27225.3730718743)) + U[22]*U[24]*(15.5*n*(0.4375*n**2 + 15.75)/(3.875*n**2 + 27225.3730718743) - 27.125*n*(n**2 + 1.0)/(3.875*n**2 + 27225.3730718743)) + 0.00300387596899225*U[23]*(-0.25*n**2 - 16.0)/(3.875*n**2 + 27225.3730718743) - 1.64361941021061*U[27]*n/(3.875*n**2 + 27225.3730718743)\n", - "\tU_out[24] = 0.00150193798449612*U[12]*(-1.0*n**2 - 1.0)/(15.5*n**2 + 26992.8730718743) + U[20]*U[21]*(-15.5*n*(0.1875*n**2 + 0.75)/(15.5*n**2 + 26992.8730718743) + 15.5*n*(0.1875*n**2 + 3.0)/(15.5*n**2 + 26992.8730718743)) + 1.64361941021061*U[20]*n/(15.5*n**2 + 26992.8730718743) + U[21]*U[22]*(-15.5*n*(0.3125*n**2 + 5.0)/(15.5*n**2 + 26992.8730718743) + 15.5*n*(0.3125*n**2 + 11.25)/(15.5*n**2 + 26992.8730718743)) + U[22]*U[23]*(-15.5*n*(0.4375*n**2 + 15.75)/(15.5*n**2 + 26992.8730718743) + 15.5*n*(0.4375*n**2 + 28.0)/(15.5*n**2 + 26992.8730718743)) + 0.00300387596899225*U[24]*(-1.0*n**2 - 1.0)/(15.5*n**2 + 26992.8730718743) - 0.00150193798449612*U[2]*(-1.0*n**2 - 1.0)/(15.5*n**2 + 26992.8730718743)\n", - "\tU_out[25] = 0.00150193798449612*U[15]*(-1.0*n**2 - 4.0)/(15.5*n**2 + 27039.3730718743) + U[20]*U[22]*(-15.5*n*(0.25*n**2 + 1.0)/(15.5*n**2 + 27039.3730718743) + 15.5*n*(0.25*n**2 + 9.0)/(15.5*n**2 + 27039.3730718743)) + U[21]*U[23]*(-15.5*n*(0.375*n**2 + 6.0)/(15.5*n**2 + 27039.3730718743) + 15.5*n*(0.375*n**2 + 24.0)/(15.5*n**2 + 27039.3730718743)) + 1.64361941021061*U[21]*n/(15.5*n**2 + 27039.3730718743) + 0.00300387596899225*U[25]*(-1.0*n**2 - 4.0)/(15.5*n**2 + 27039.3730718743) - 0.00150193798449612*U[5]*(-1.0*n**2 - 4.0)/(15.5*n**2 + 27039.3730718743)\n", - "\tU_out[26] = U[20]*U[21]*(15.5*n*(0.0625*n**2 + 0.25)/(15.5*n**2 + 27116.8730718743) - 15.5*n*(0.0625*n**2 + 1.0)/(15.5*n**2 + 27116.8730718743)) + U[20]*U[23]*(-15.5*n*(0.3125*n**2 + 1.25)/(15.5*n**2 + 27116.8730718743) + 15.5*n*(0.3125*n**2 + 20.0)/(15.5*n**2 + 27116.8730718743)) + 1.64361941021061*U[22]*n/(15.5*n**2 + 27116.8730718743) + 0.00300387596899225*U[26]*(-1.0*n**2 - 9.0)/(15.5*n**2 + 27116.8730718743)\n", - "\tU_out[27] = U[20]*U[22]*(15.5*n*(0.125*n**2 + 0.5)/(15.5*n**2 + 27225.3730718743) - 15.5*n*(0.125*n**2 + 4.5)/(15.5*n**2 + 27225.3730718743)) + 1.64361941021061*U[23]*n/(15.5*n**2 + 27225.3730718743) + 0.00300387596899225*U[27]*(-1.0*n**2 - 16.0)/(15.5*n**2 + 27225.3730718743)\n", - "\tU_out[28] = U[11]*(-0.000183587669699814*epsilon - 0.000822506165849588) + U[13]*(-0.000165286974476004*epsilon - 0.000740515721254207) + U[16]*(-3.67175339399628e-5*epsilon - 0.000164501233169918) + 0.75*U[21]*U[32]*n - 0.25*U[21]*U[34]*n + 1.0*U[22]*U[33]*n - 0.5*U[22]*U[35]*n + 1.25*U[23]*U[34]*n - 0.75*U[24]*U[29]*n - 1.0*U[25]*U[30]*n + 0.25*U[26]*U[29]*n - 1.25*U[26]*U[31]*n + 0.5*U[27]*U[30]*n - 0.00122336344718992*U[28]\n", - "\tU_out[29] = U[10]*(0.000330573948952008*epsilon + 0.00148103144250841) + U[14]*(-0.000183587669699814*epsilon - 0.000822506165849588) + U[18]*(-3.67175339399628e-5*epsilon - 0.000164501233169918) - 0.75*U[20]*U[32]*n + 0.25*U[20]*U[34]*n + 1.25*U[22]*U[32]*n + 1.5*U[23]*U[33]*n + 0.75*U[24]*U[28]*n - 1.25*U[24]*U[30]*n - 1.5*U[25]*U[31]*n - 0.25*U[26]*U[28]*n - 0.00122336344718992*U[29] + 0.000137893034416115\n", - "\tU_out[30] = U[13]*(0.000297516554056807*epsilon + 0.00133292829825757) - 1.0*U[20]*U[33]*n + 0.5*U[20]*U[35]*n - 1.25*U[21]*U[32]*n + 1.75*U[23]*U[32]*n + 1.25*U[24]*U[29]*n - 1.75*U[24]*U[31]*n + 1.0*U[25]*U[28]*n - 0.5*U[27]*U[28]*n - 0.00122336344718992*U[30]\n", - "\tU_out[31] = U[10]*(0.000132229579580803*epsilon + 0.000592412577003366) - 1.25*U[20]*U[34]*n - 1.5*U[21]*U[33]*n - 1.75*U[22]*U[32]*n + 1.75*U[24]*U[30]*n + 1.5*U[25]*U[29]*n + 1.25*U[26]*U[28]*n - 0.00122336344718992*U[31] + 5.51572137664459e-5\n", - "\tU_out[32] = U[12]*(0.000432568255813953*epsilon + 0.00193798449612403) + 0.75*U[20]*U[29]*n - 0.75*U[21]*U[28]*n + 1.25*U[21]*U[30]*n - 1.25*U[22]*U[29]*n + 1.75*U[22]*U[31]*n - 1.75*U[23]*U[30]*n - 0.00122336344718992*U[32]\n", - "\tU_out[33] = U[15]*(0.000432568255813953*epsilon + 0.00193798449612403) + 1.0*U[20]*U[30]*n + 1.5*U[21]*U[31]*n - 1.0*U[22]*U[28]*n - 1.5*U[23]*U[29]*n - 0.00122336344718992*U[33]\n", - "\tU_out[34] = -0.25*U[20]*U[29]*n + 1.25*U[20]*U[31]*n + 0.25*U[21]*U[28]*n - 1.25*U[23]*U[28]*n - 0.00122336344718992*U[34]\n", - "\tU_out[35] = -0.5*U[20]*U[30]*n + 0.5*U[22]*U[28]*n - 0.00122336344718992*U[35]\n", - "\treturn U_out" + "\tF = np.empty_like(U)\n", + "\tF[0] = -0.02*U[0] + 0.02*U[10] + 0.0697343654705087*U[21] + 0.1012581197243*U[23]\n", + "\tF[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[10]*U[12] + 0.02*U[11] - 1.9945469157942*U[13]*U[15] - 2.59615384615385*U[14]*U[17] + 2.59615384615385*U[15]*U[16] - 0.02*U[1] - 0.00408089597671527*U[20] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", + "\tF[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[10]*U[11] + 0.02*U[12] + 1.9945469157942*U[13]*U[14] + 2.59615384615385*U[14]*U[16] + 2.59615384615385*U[15]*U[17] - 0.115315741885204*U[1] + 0.02*U[24] - 0.02*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", + "\tF[3] = 2.16075915877706*U[11]*U[15] - 2.16075915877706*U[12]*U[14] + 0.02*U[13] + 4.32151831755411*U[16]*U[19] - 4.32151831755411*U[17]*U[18] + 2.16075915877706*U[1]*U[5] - 0.00298520400130602*U[20] + 0.0328850072783872*U[22] - 2.16075915877706*U[2]*U[4] - 0.02*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", + "\tF[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[10]*U[15] + 2.43*U[11]*U[17] + 0.345721465404329*U[12]*U[13] - 2.43*U[12]*U[16] + 0.02*U[14] + 2.43*U[1]*U[7] - 0.00619643245104446*U[21] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.02*U[4] + 0.059964185780306*U[5]\n", + "\tF[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[10]*U[14] - 0.345721465404329*U[11]*U[13] - 2.43*U[11]*U[16] - 2.43*U[12]*U[17] + 0.02*U[15] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.02*U[25] - 2.43*U[2]*U[7] - 0.059964185780306*U[4] - 0.02*U[5]\n", + "\tF[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[10]*U[17] + 0.675*U[11]*U[15] + 0.675*U[12]*U[14] - 5.18582198106493*U[13]*U[19] + 0.02*U[16] + 0.675*U[1]*U[5] - 0.000265258238486493*U[20] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.02*U[6] + 0.0749552322253824*U[7]\n", + "\tF[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[10]*U[16] - 0.675*U[11]*U[14] + 0.675*U[12]*U[15] + 5.18582198106493*U[13]*U[18] + 0.02*U[17] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.02*U[7]\n", + "\tF[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[10]*U[19] - 2.65939588772561*U[13]*U[17] + 0.02*U[18] - 0.00059581081260043*U[21] - 2.65939588772561*U[3]*U[7] - 0.02*U[8] + 0.0576578709426019*U[9]\n", + "\tF[9] = 2.6593958877256*U[0]*U[8] + 2.6593958877256*U[10]*U[18] + 2.6593958877256*U[13]*U[16] + 0.02*U[19] + 2.6593958877256*U[3]*U[6] - 0.0576578709426019*U[8] - 0.02*U[9]\n", + "\tF[10] = 0.00181818181818182*U[0] - 0.0326124628611698*U[10] - 1.63693875664928*U[11]*U[2] + 1.63693875664928*U[12]*U[1] - 1.30955100531943*U[14]*U[5] + 1.30955100531943*U[15]*U[4] - 3.27387751329857*U[16]*U[7] + 3.27387751329857*U[17]*U[6] - 2.61910201063885*U[18]*U[9] + 2.61910201063885*U[19]*U[8] - 0.00633948777004624*U[21] - 0.00920528361130002*U[23] + 0.00805846274736615*U[29] + 0.00322338509894646*U[31] + 0.000468575805854495\n", + "\tF[11] = -1.6647358298754*U[0]*U[12] + 1.05320021890077*U[10]*U[2] - 0.0440556295451221*U[11] + 0.0282849932925971*U[12] + 1.68512035024123*U[13]*U[5] + 1.06132075471698*U[14]*U[7] - 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] + 2.33490566037736*U[16]*U[5] - 2.33490566037736*U[17]*U[4] + 0.00490566037735849*U[1] + 0.00100097448485469*U[20] - 0.00371538559555317*U[28]\n", + "\tF[12] = 1.6647358298754*U[0]*U[11] - 1.05320021890077*U[10]*U[1] - 0.0282849932925971*U[11] - 0.0440556295451221*U[12] - 1.68512035024123*U[13]*U[4] + 2.66357732780065*U[14]*U[3] - 1.06132075471698*U[14]*U[6] - 1.06132075471698*U[15]*U[7] + 2.33490566037736*U[16]*U[4] + 2.33490566037736*U[17]*U[5] - 0.00490566037735849*U[24] + 0.00490566037735849*U[2] + 0.00875417106918239*U[32]\n", + "\tF[13] = -1.44050610585137*U[11]*U[5] + 1.44050610585137*U[12]*U[4] - 0.0470526493909192*U[13] - 2.67522562515255*U[14]*U[2] + 2.67522562515255*U[15]*U[1] - 2.88101221170274*U[16]*U[9] + 2.88101221170274*U[17]*U[8] - 5.35045125030509*U[18]*U[7] + 5.35045125030509*U[19]*U[6] + 0.000852915428944578*U[20] - 0.00939571636525347*U[22] - 0.00316582465075099*U[28] + 0.00569848437135178*U[30] + 0.00571428571428572*U[3]\n", + "\tF[14] = -1.35185957626052*U[0]*U[15] + 0.421071015556554*U[10]*U[5] - 0.449999999999999*U[11]*U[7] - 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 1.90590038620335*U[13]*U[2] - 0.054383821013715*U[14] + 0.0230631483770408*U[15] - 2.31923076923077*U[16]*U[2] + 2.31923076923077*U[17]*U[1] + 0.00238324325040172*U[21] - 0.00302946825483566*U[29] + 0.00769230769230769*U[4]\n", + "\tF[15] = 1.35185957626052*U[0]*U[14] - 0.421071015556554*U[10]*U[4] + 1.63996079743079*U[11]*U[3] + 0.45*U[11]*U[6] + 0.45*U[12]*U[7] - 1.90590038620335*U[13]*U[1] - 0.0230631483770408*U[14] - 0.054383821013715*U[15] - 2.31923076923077*U[16]*U[1] - 2.31923076923077*U[17]*U[2] - 0.00769230769230769*U[25] + 0.00713801641025641*U[33] + 0.00769230769230769*U[5]\n", + "\tF[16] = -3.421202001397*U[0]*U[17] + 0.180063263231421*U[10]*U[7] - 0.7875*U[11]*U[5] - 0.7875*U[12]*U[4] + 0.288101221170274*U[13]*U[9] + 1.4625*U[14]*U[2] + 1.4625*U[15]*U[1] - 0.0629368545736434*U[16] + 0.0374776161126912*U[17] - 5.47392320223521*U[19]*U[3] + 0.000132629119243246*U[20] - 0.000492288591410796*U[28] + 0.01*U[6]\n", + "\tF[17] = 3.421202001397*U[0]*U[16] - 0.180063263231421*U[10]*U[6] + 0.7875*U[11]*U[4] - 0.7875*U[12]*U[5] - 0.288101221170275*U[13]*U[8] - 1.4625*U[14]*U[1] + 1.4625*U[15]*U[2] - 0.0374776161126912*U[16] - 0.0629368545736434*U[17] + 5.4739232022352*U[18]*U[3] + 0.01*U[7]\n", + "\tF[18] = -2.75575081119392*U[0]*U[19] - 0.250522801017629*U[10]*U[9] + 1.00209120407052*U[13]*U[7] - 4.00836481628207*U[17]*U[3] - 0.0677711778901247*U[18] + 0.0325892314023402*U[19] + 0.000336762633208938*U[21] - 0.000428077036009388*U[29] + 0.011304347826087*U[8]\n", + "\tF[19] = 2.75575081119392*U[0]*U[18] + 0.250522801017629*U[10]*U[8] - 1.00209120407052*U[13]*U[6] + 4.00836481628207*U[16]*U[3] - 0.0325892314023402*U[18] - 0.0677711778901247*U[19] + 0.011304347826087*U[9]\n", + "\tF[20] = 7.67246397508814e-8*U[11] + 8.50171631003011e-8*U[13] + 4.72151629236194e-8*U[16] - 7.67246397508814e-8*U[1] - 1.73825166772134e-7*U[20] - 0.000847606278214279*U[21]*U[24] - 0.00143958526617346*U[21]*U[26] - 0.0028522623965306*U[22]*U[25] - 0.00374023087846937*U[22]*U[27] - 0.00571797886096935*U[23]*U[26] - 9.13068063386782e-5*U[24] - 8.50171631003011e-8*U[3] - 4.72151629236194e-8*U[6]\n", + "\tF[21] = 4.24355024932212e-8*U[0] - 4.24355024932212e-8*U[10] + 1.4729372656196e-7*U[14] + 6.12741902497755e-8*U[18] - 0.00108790599513578*U[20]*U[24] + 0.00208179542279068*U[20]*U[26] - 5.06696893769175e-7*U[21] - 0.00678262379683416*U[22]*U[24] - 0.0132966288294373*U[23]*U[25] - 9.11498353211769e-5*U[25] - 1.4729372656196e-7*U[4] - 6.12741902497755e-8*U[8]\n", + "\tF[22] = -1.5233133904442e-7*U[13] - 0.00401776948188515*U[20]*U[25] + 0.00715162967775557*U[20]*U[27] + 0.00140621931865981*U[21]*U[24] - 1.05894699522554e-6*U[22] - 0.0199683143249692*U[23]*U[24] - 9.08894129022384e-5*U[26] + 1.5233133904442e-7*U[3]\n", + "\tF[23] = 1.68582727186388e-8*U[0] - 1.68582727186388e-8*U[10] - 0.0103378871816782*U[20]*U[26] - 0.0021609519012024*U[21]*U[25] + 0.00943082095154381*U[22]*U[24] - 1.82681677295638e-6*U[23] - 9.05273115564549e-5*U[27]\n", + "\tF[24] = -1.80603224383758e-7*U[12] + 0.00193551086316502*U[20]*U[21] + 9.12184436809035e-5*U[20] + 0.00537641906434727*U[21]*U[22] + 0.0105377813661206*U[22]*U[23] - 3.61206448767517e-7*U[24] + 1.80603224383758e-7*U[2]\n", + "\tF[25] = -3.46717381704589e-7*U[15] + 0.00686999688804741*U[20]*U[22] + 0.0154574929981067*U[21]*U[23] + 9.10617760748484e-5*U[21] - 6.93434763409178e-7*U[25] + 3.46717381704589e-7*U[5]\n", + "\tF[26] = -0.000642223843335634*U[20]*U[21] + 0.0160555960833909*U[20]*U[23] + 9.08018558801295e-5*U[22] - 1.24461985142565e-6*U[26]\n", + "\tF[27] = -0.00341156103036174*U[20]*U[22] + 9.04404504616234e-5*U[23] - 2.01101385026168e-6*U[27]\n", + "\tF[28] = -0.000960310243846486*U[11] - 0.0008645829811078*U[13] - 0.000192062048769297*U[16] + 1.125*U[21]*U[32] - 0.375000000000001*U[21]*U[34] + 1.5*U[22]*U[33] - 0.75*U[22]*U[35] + 1.875*U[23]*U[34] - 1.125*U[24]*U[29] - 1.5*U[25]*U[30] + 0.375000000000001*U[26]*U[29] - 1.875*U[26]*U[31] + 0.75*U[27]*U[30] - 0.0012202230620155*U[28]\n", + "\tF[29] = 0.0017291659622156*U[10] - 0.000960310243846486*U[14] - 0.000192062048769297*U[18] - 1.125*U[20]*U[32] + 0.375*U[20]*U[34] + 1.875*U[22]*U[32] + 2.25*U[23]*U[33] + 1.125*U[24]*U[28] - 1.875*U[24]*U[30] - 2.25*U[25]*U[31] - 0.375*U[26]*U[28] - 0.0012202230620155*U[29] + 0.000137865173161608\n", + "\tF[30] = 0.00155624936599404*U[13] - 1.5*U[20]*U[33] + 0.75*U[20]*U[35] - 1.875*U[21]*U[32] + 2.625*U[23]*U[32] + 1.875*U[24]*U[29] - 2.625*U[24]*U[31] + 1.5*U[25]*U[28] - 0.75*U[27]*U[28] - 0.0012202230620155*U[30]\n", + "\tF[31] = 0.00069166638488624*U[10] - 1.875*U[20]*U[34] - 2.25*U[21]*U[33] - 2.625*U[22]*U[32] + 2.625*U[24]*U[30] + 2.25*U[25]*U[29] + 1.875*U[26]*U[28] - 0.0012202230620155*U[31] + 5.51460692646433e-5\n", + "\tF[32] = 0.00226267770542636*U[12] + 1.125*U[20]*U[29] - 1.125*U[21]*U[28] + 1.875*U[21]*U[30] - 1.875*U[22]*U[29] + 2.625*U[22]*U[31] - 2.625*U[23]*U[30] - 0.0012202230620155*U[32]\n", + "\tF[33] = 0.00226267770542636*U[15] + 1.5*U[20]*U[30] + 2.25*U[21]*U[31] - 1.5*U[22]*U[28] - 2.25*U[23]*U[29] - 0.0012202230620155*U[33]\n", + "\tF[34] = -0.375*U[20]*U[29] + 1.875*U[20]*U[31] + 0.375*U[21]*U[28] - 1.875*U[23]*U[28] - 0.0012202230620155*U[34]\n", + "\tF[35] = -0.75*U[20]*U[30] + 0.75*U[22]*U[28] - 0.0012202230620155*U[35]\n", + "\treturn F" ] }, { diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 5c47144..3439555 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -246,7 +246,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): subs = self.subs + self.atmospheric_basis.substitutions + self.oceanic_basis.substitutions else: subs = self.subs - print(subs) + noc = len(ocean_basis) if self.return_symbolic: self._gh = None From af0b5a53a8feab065682808083713a330238cb30 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 30 Aug 2023 14:10:01 +0200 Subject: [PATCH 052/143] Added a property symbol for the parameter class --- qgs/params/parameter.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 21f0f84..39689b5 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -62,6 +62,8 @@ class Parameter(float): `None` by default. If `None`, cannot transform between dimensional and nondimentional value. description: str, optional String describing the parameter. + symbol: sympy.core.symbol.Symbol, optional + A `Sympy`_ symbol to represent the parameter in symbolic expressions. return_dimensional: bool, optional Defined if the value returned by the parameter is dimensional or not. Default to `False`. @@ -74,10 +76,11 @@ class Parameter(float): -------- If no scale_object argument is provided, cannot transform between the dimensional and nondimentional value ! + .. _Sympy: https://www.sympy.org/ """ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, description="", - return_dimensional=False): + symbol=None, return_dimensional=False): no_scale = False @@ -102,7 +105,9 @@ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, des else: evalue = value - if no_scale: + scale_diff = input_dimensional ^ return_dimensional + + if no_scale and scale_diff: warnings.warn("Parameter configured to perform dimensional conversion " + "but without specifying a ScaleParams object: Conversion disabled!") @@ -112,6 +117,7 @@ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, des f._units = units f._scale_object = scale_object f._description = description + f._symbol = symbol return f @@ -131,6 +137,11 @@ def nondimensional_value(self): else: return self + @property + def symbol(self): + """sympy.core.symbol.Symbol: Returns the symbol of the parameter.""" + return self._symbol + @property def input_dimensional(self): """bool: Indicate if the provided value is dimensional or not.""" From 41f066a9a1046a6ff331491228920f36c9344399 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 30 Aug 2023 14:20:06 +0200 Subject: [PATCH 053/143] Reverting some of the previous changes --- qgs/params/parameter.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 39689b5..19d374a 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -105,9 +105,7 @@ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, des else: evalue = value - scale_diff = input_dimensional ^ return_dimensional - - if no_scale and scale_diff: + if no_scale: warnings.warn("Parameter configured to perform dimensional conversion " + "but without specifying a ScaleParams object: Conversion disabled!") From fa50cab4f13fb8dbdb9e28fb6b7dbb3f667b5e01 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Thu, 31 Aug 2023 13:25:52 +0200 Subject: [PATCH 054/143] Created a ScalingParameter to address a longstanding problem --- qgs/inner_products/symbolic.py | 1 + qgs/params/parameter.py | 63 ++++++++++++++++++++++++++++++++++ qgs/params/params.py | 53 +++++++++++++++++++--------- 3 files changed, 101 insertions(+), 16 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 3439555..c45a5c9 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -1703,6 +1703,7 @@ def _parallel_compute(pool, args_list, subs, destination, timeout, permute=False if return_dict: return destination + if __name__ == '__main__': from qgs.params.params import QgParams pars = QgParams(dynamic_T=True) # , T4=True) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 19d374a..4922b51 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -44,6 +44,63 @@ import warnings +class ScalingParameter(float): + """Class of model's dimension parameter. + + Parameters + ---------- + value: float + Value of the parameter. + units: str, optional + The units of the provided value. Used to compute the conversion between dimensional and nondimensional + value. Should be specified by joining atoms like `'[unit^power]'`, e.g '`[m^2][s^-2][Pa^-2]'`. + Empty by default. + description: str, optional + String describing the parameter. + symbol: sympy.core.symbol.Symbol, optional + A `Sympy`_ symbol to represent the parameter in symbolic expressions. + dimensional: bool, optional + Indicate if the value of the parameter is dimensional or not. Default to `True`. + + Notes + ----- + Parameter is immutable. Once instantiated, it cannot be altered. To create a new parameter, one must + re-instantiate it. + + .. _Sympy: https://www.sympy.org/ + """ + + def __new__(cls, value, units="", description="", symbol=None, dimensional=False): + + f = float.__new__(cls, value) + f._dimensional = dimensional + f._units = units + f._description = description + f._symbol = symbol + + return f + + @property + def symbol(self): + """sympy.core.symbol.Symbol: Returns the symbol of the parameter.""" + return self._symbol + + @property + def dimensional(self): + """bool: Indicate if the returned value is dimensional or not.""" + return self._dimensional + + @property + def units(self): + """str: The units of the dimensional value.""" + return self._units + + @property + def description(self): + """str: Description of the parameter.""" + return self._description + + class Parameter(float): """Base class of model's parameter. @@ -108,6 +165,12 @@ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, des if no_scale: warnings.warn("Parameter configured to perform dimensional conversion " + "but without specifying a ScaleParams object: Conversion disabled!") + print(input_dimensional) + print(return_dimensional) + print(no_scale) + print(description) + print(value) + raise Exception f = float.__new__(cls, evalue) f._input_dimensional = input_dimensional diff --git a/qgs/params/params.py b/qgs/params/params.py index 687ca8b..3e8af9c 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -46,14 +46,14 @@ import warnings from abc import ABC -from qgs.params.parameter import Parameter +from qgs.params.parameter import Parameter, ScalingParameter from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis import sympy as sy # TODO: - store model version in a variable somewhere -# - force the user to define the aspect ratio n at parameter object instantiation +# - force or warn the user to define the aspect ratio n at parameter object instantiation class Params(ABC): @@ -89,11 +89,23 @@ def set_params(self, dic): self.__dict__[key] = val else: d = self.__dict__[key].__dict__ - self.__dict__[key] = Parameter(val, input_dimensional=d['_input_dimensional'], + self.__dict__[key] = Parameter(val, + input_dimensional=d['_input_dimensional'], units=d['_units'], description=d['_description'], scale_object=d['_scale_object'], + symbol=d['_symbol'], return_dimensional=d['_return_dimensional']) + elif isinstance(self.__dict__[key], ScalingParameter): + if isinstance(val, ScalingParameter): + self.__dict__[key] = val + else: + d = self.__dict__[key].__dict__ + self.__dict__[key] = ScalingParameter(val, + units=d['_units'], + description=d['_description'], + symbol=d['_symbol'], + dimensional=d['_dimensional']) else: self.__dict__[key] = val @@ -114,6 +126,12 @@ def __str__(self): else: units = "[nondim]" s += "'" + key + "': " + str(efval) + " " + units + " (" + val.description + "),\n" + elif isinstance(val, ScalingParameter): + if val.dimensional: + units = val.units + else: + units = "[nondim]" + s += "'" + key + "': " + str(val) + " " + units + " (" + val.description + "),\n" elif isinstance(val, (np.ndarray, list, tuple)) and isinstance(val[0], Parameter): for i, v in enumerate(val): if v.input_dimensional: @@ -139,7 +157,7 @@ def print_params(self): @staticmethod def create_params_array(values, input_dimensional=None, units=None, scale_object=None, description=None, - return_dimensional=None): + return_dimensional=None, symbol=None): if hasattr(values, "__iter__"): ls = len(values) @@ -165,6 +183,10 @@ def create_params_array(values, input_dimensional=None, units=None, scale_object s = ls * [scale_object] else: s = scale_object + if not isinstance(symbol, list): + sy = ls * [symbol] + else: + sy = symbol if not isinstance(return_dimensional, list): if return_dimensional is None: return_dimensional = False @@ -174,7 +196,7 @@ def create_params_array(values, input_dimensional=None, units=None, scale_object arr = list() for i, val in enumerate(values): arr.append(Parameter(val, input_dimensional=idx[i], units=u[i], scale_object=s[i], description=d[i], - return_dimensional=rd[i])) + return_dimensional=rd[i], symbol=sy)) else: arr = values * [Parameter(0.e0, input_dimensional=input_dimensional, units=units, scale_object=scale_object, description=description, return_dimensional=return_dimensional)] @@ -250,17 +272,16 @@ def __init__(self, dic=None): # Scale parameters for the ocean and the atmosphere # ----------------------------------------------------------- - self.scale = Parameter(5.e6, units='[m]', description="characteristic space scale (L*pi)", - return_dimensional=True) - self.f0 = Parameter(1.032e-4, units='[s^-1]', description="Coriolis parameter at the middle of the domain", - return_dimensional=True) - self.n = Parameter(1.3e0, input_dimensional=False, description="aspect ratio (n = 2 L_y / L_x)") - self.rra = Parameter(6370.e3, units='[m]', description="earth radius", return_dimensional=True) - self.phi0_npi = Parameter(0.25e0, input_dimensional=False, description="latitude expressed in fraction of pi") - self.deltap = Parameter(5.e4, units='[Pa]', description='pressure difference between the two atmospheric layers', - return_dimensional=True) - self.Ha = Parameter(8500., units='[m]', description="Average height of the 500 hPa pressure level at midlatitude", - return_dimensional=True) + self.scale = ScalingParameter(5.e6, units='[m]', description="characteristic space scale (L*pi)", dimensional=True) + self.f0 = ScalingParameter(1.032e-4, units='[s^-1]', description="Coriolis parameter at the middle of the domain", + dimensional=True) + self.n = ScalingParameter(1.3e0, dimensional=False, description="aspect ratio (n = 2 L_y / L_x)") + self.rra = ScalingParameter(6370.e3, units='[m]', description="earth radius", dimensional=True) + self.phi0_npi = ScalingParameter(0.25e0, dimensional=False, description="latitude expressed in fraction of pi") + self.deltap = ScalingParameter(5.e4, units='[Pa]', description='pressure difference between the two atmospheric layers', + dimensional=True) + self.Ha = ScalingParameter(8500., units='[m]', description="Average height of the 500 hPa pressure level at midlatitude", + dimensional=True) self.set_params(dic) # ---------------------------------------- From 4d1d2e2d0634a0e737237d221285496ce4105f37 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Thu, 31 Aug 2023 17:35:43 +0200 Subject: [PATCH 055/143] Implemented an array parameters class --- qgs/params/parameter.py | 183 +++++++++++++++++++++++++++++++++++++--- qgs/params/params.py | 90 +++++--------------- 2 files changed, 193 insertions(+), 80 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 4922b51..c3a1362 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -9,7 +9,8 @@ -------- >>> from qgs.params.params import ScaleParams - >>> from qgs.params.parameter import Parameter + >>> from qgs.params.parameter import Parameter, ArrayParameters + >>> import numpy as np >>> # defining a scale object to help Parameter compute the nondimensionalization >>> sc = ScaleParams() >>> # creating a parameter initialized with a nondimensional value but returning a @@ -36,12 +37,24 @@ 2.1581898457499433e-06 >>> sigma.return_dimensional False + >>> # creating a parameters array initialized with a nondimensional values and returning + >>> # nondimensional ones when called + >>> s = ArrayParameters(np.array([[0.1,0.2],[0.3,0.4]]), input_dimensional=False, scale_object=sc, units='[s^-1]', + ... description="atmosphere bottom friction coefficient") + >>> s + ArrayParameters([[0.1, 0.2], + [0.3, 0.4]], dtype=object) + >>> # dimensional values can also be retrieved with + >>> s.dimensional_values + array([[1.0320000000000001e-05, 2.0640000000000002e-05], + [3.096e-05, 4.1280000000000005e-05]], dtype=object) Main class ---------- """ import warnings +import numpy as np class ScalingParameter(float): @@ -57,7 +70,7 @@ class ScalingParameter(float): Empty by default. description: str, optional String describing the parameter. - symbol: sympy.core.symbol.Symbol, optional + symbol: ~sympy.core.symbol.Symbol, optional A `Sympy`_ symbol to represent the parameter in symbolic expressions. dimensional: bool, optional Indicate if the value of the parameter is dimensional or not. Default to `True`. @@ -82,7 +95,7 @@ def __new__(cls, value, units="", description="", symbol=None, dimensional=False @property def symbol(self): - """sympy.core.symbol.Symbol: Returns the symbol of the parameter.""" + """~sympy.core.symbol.Symbol: Returns the symbol of the parameter.""" return self._symbol @property @@ -119,7 +132,7 @@ class Parameter(float): `None` by default. If `None`, cannot transform between dimensional and nondimentional value. description: str, optional String describing the parameter. - symbol: sympy.core.symbol.Symbol, optional + symbol: ~sympy.core.symbol.Symbol, optional A `Sympy`_ symbol to represent the parameter in symbolic expressions. return_dimensional: bool, optional Defined if the value returned by the parameter is dimensional or not. Default to `False`. @@ -165,12 +178,6 @@ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, des if no_scale: warnings.warn("Parameter configured to perform dimensional conversion " + "but without specifying a ScaleParams object: Conversion disabled!") - print(input_dimensional) - print(return_dimensional) - print(no_scale) - print(description) - print(value) - raise Exception f = float.__new__(cls, evalue) f._input_dimensional = input_dimensional @@ -200,7 +207,7 @@ def nondimensional_value(self): @property def symbol(self): - """sympy.core.symbol.Symbol: Returns the symbol of the parameter.""" + """~sympy.core.symbol.Symbol: Returns the symbol of the parameter.""" return self._symbol @property @@ -253,3 +260,157 @@ def _nondimensionalization(self): return self._conversion_factor(self._units, self._scale_object) +class ArrayParameters(np.ndarray): + """Base class of model's array of parameters. + + Parameters + ---------- + values: list(float) or ~numpy.ndarray(float) or list(Parameter) or ~numpy.ndarray(Parameter) + Values of the parameter array. + input_dimensional: bool, optional + Specify whether the value provided is dimensional or not. Default to `True`. + units: str, optional + The units of the provided value. Used to compute the conversion between dimensional and nondimensional + value. Should be specified by joining atoms like `'[unit^power]'`, e.g '`[m^2][s^-2][Pa^-2]'`. + Empty by default. + scale_object: ScaleParams, optional + A scale parameters object to compute the conversion between dimensional and nondimensional value. + `None` by default. If `None`, cannot transform between dimensional and nondimentional value. + description: str or list(str) or array(str), optional + String or an iterable of string, describing the parameters. + If an iterable, should have the same length or shape as `values`. + symbol: ~sympy.core.symbol.Symbol or list(~sympy.core.symbol.Symbol) or ~numpy.ndarray(~sympy.core.symbol.Symbol), optional + A `Sympy`_ symbol or an iterable of, to represent the parameters in symbolic expressions. + If an iterable, should have the same length or shape as `values`. + return_dimensional: bool, optional + Defined if the value returned by the parameter is dimensional or not. Default to `False`. + + Warnings + -------- + If no scale_object argument is provided, cannot transform between the dimensional and nondimensional value ! + + .. _Sympy: https://www.sympy.org/ + """ + + def __new__(cls, values, input_dimensional=True, units="", scale_object=None, description="", + symbol=None, return_dimensional=False): + + if isinstance(values, (tuple, list)): + new_arr = np.empty(len(values), dtype=object) + for i, val in enumerate(values): + if isinstance(description, (tuple, list, np.ndarray)): + descr = description[i] + else: + descr = description + if isinstance(symbol, (tuple, list, np.ndarray)): + sy = symbol[i] + else: + sy = symbol + new_arr[i] = Parameter(val, input_dimensional=input_dimensional, units=units, scale_object=scale_object, description=descr, + return_dimensional=return_dimensional, symbol=sy) + else: + new_arr = np.empty_like(values, dtype=object) + for idx in np.ndindex(values.shape): + if isinstance(description, np.ndarray): + descr = description[idx] + else: + descr = description + if isinstance(symbol, np.ndarray): + sy = symbol[idx] + else: + sy = symbol + new_arr[idx] = Parameter(values[idx], input_dimensional=input_dimensional, units=units, scale_object=scale_object, description=descr, + return_dimensional=return_dimensional, symbol=sy) + arr = np.asarray(new_arr).view(cls) + arr._input_dimensional = input_dimensional + arr._return_dimensional = return_dimensional + arr._units = units + arr._scale_object = scale_object + + return arr + + def __array_finalize__(self, arr): + + if arr is None: + return + + self._input_dimensional = getattr(arr, '_input_dimensional', True) + self._units = getattr(arr, '_units', "") + self._return_dimensional = getattr(arr, '_return_dimensional', False) + self._scale_object = getattr(arr, '_scale_object', None) + + @property + def dimensional_values(self): + """float: Returns the dimensional value.""" + if self._return_dimensional: + return self + else: + return np.array(self / self._nondimensionalization) + + @property + def nondimensional_values(self): + """float: Returns the nondimensional value.""" + if self._return_dimensional: + return np.array(self * self._nondimensionalization) + else: + return self + + @property + def symbols(self): + """~numpy.ndarray(~sympy.core.symbol.Symbol): Returns the symbol of the parameters in the array.""" + symbols = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + symbols[idx] = self[idx].symbol + return symbols + + @property + def input_dimensional(self): + """bool: Indicate if the provided value is dimensional or not.""" + return self._input_dimensional + + @property + def return_dimensional(self): + """bool: Indicate if the returned value is dimensional or not.""" + return self._return_dimensional + + @classmethod + def _conversion_factor(cls, units, scale_object): + factor = 1. + + ul = units.split('][') + ul[0] = ul[0][1:] + ul[-1] = ul[-1][:-1] + + for us in ul: + up = us.split('^') + if len(up) == 1: + up.append("1") + + if up[0] == 'm': + factor *= scale_object.L ** (-int(up[1])) + elif up[0] == 's': + factor *= scale_object.f0 ** (int(up[1])) + elif up[0] == 'Pa': + factor *= scale_object.deltap ** (-int(up[1])) + + return factor + + @property + def units(self): + """str: The units of the dimensional value.""" + return self._units + + @property + def descriptions(self): + """~numpy.ndarray(str): Description of the parameters in the array.""" + descr = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + descr[idx] = self[idx].description + return descr + + @property + def _nondimensionalization(self): + if self._scale_object is None: + return 1. + else: + return self._conversion_factor(self._units, self._scale_object) diff --git a/qgs/params/params.py b/qgs/params/params.py index 3e8af9c..d1738b8 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -46,7 +46,7 @@ import warnings from abc import ABC -from qgs.params.parameter import Parameter, ScalingParameter +from qgs.params.parameter import Parameter, ScalingParameter, ArrayParameters from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis @@ -155,54 +155,6 @@ def print_params(self): """Print the parameters contained in the container.""" print(self._list_params()) - @staticmethod - def create_params_array(values, input_dimensional=None, units=None, scale_object=None, description=None, - return_dimensional=None, symbol=None): - - if hasattr(values, "__iter__"): - ls = len(values) - if not isinstance(input_dimensional, list): - if input_dimensional is None: - input_dimensional = True - idx = ls * [input_dimensional] - else: - idx = input_dimensional - if not isinstance(units, list): - if units is None: - units = "" - u = ls * [units] - else: - u = units - if not isinstance(description, list): - if description is None: - description = "" - d = ls * [description] - else: - d = description - if not isinstance(scale_object, list): - s = ls * [scale_object] - else: - s = scale_object - if not isinstance(symbol, list): - sy = ls * [symbol] - else: - sy = symbol - if not isinstance(return_dimensional, list): - if return_dimensional is None: - return_dimensional = False - rd = ls * [return_dimensional] - else: - rd = return_dimensional - arr = list() - for i, val in enumerate(values): - arr.append(Parameter(val, input_dimensional=idx[i], units=u[i], scale_object=s[i], description=d[i], - return_dimensional=rd[i], symbol=sy)) - else: - arr = values * [Parameter(0.e0, input_dimensional=input_dimensional, units=units, scale_object=scale_object, - description=description, return_dimensional=return_dimensional)] - - return np.array(arr, dtype=object) - def __repr__(self): s = super(Params, self).__repr__()+"\n"+self._list_params() return s @@ -291,28 +243,28 @@ def __init__(self, dic=None): @property def L(self): """Parameter: Typical length scale :math:`L` of the model, in meters [:math:`m`].""" - return Parameter(self.scale / np.pi, units=self.scale.units, description='Typical length scale L', - return_dimensional=True) + return ScalingParameter(self.scale / np.pi, units=self.scale.units, description='Typical length scale L', + dimensional=True) @property def L_y(self): """Parameter: The meridional extent :math:`L_y = \\pi \\, L` of the model's domain, in meters [:math:`m`].""" - return Parameter(self.scale, units=self.scale.units, description='The meridional extent of the model domain', - return_dimensional=True) + return ScalingParameter(self.scale, units=self.scale.units, description='The meridional extent of the model domain', + dimensional=True) @property def L_x(self): """Parameter: The zonal extent :math:`L_x = 2 \\pi \\, L / n` of the model's domain, in meters [:math:`m`].""" - return Parameter(2 * self.scale / self.n, units=self.scale.units, - description='The zonal extent of the model domain', - return_dimensional=True) + return ScalingParameter(2 * self.scale / self.n, units=self.scale.units, + description='The zonal extent of the model domain', + dimensional=True) @property def phi0(self): """Parameter: The reference latitude :math:`\\phi_0` at the center of the domain, expressed in radians [:math:`rad`].""" - return Parameter(self.phi0_npi * np.pi, units='[rad]', - description="The reference latitude of the center of the domain", - return_dimensional=True) + return ScalingParameter(self.phi0_npi * np.pi, units='[rad]', + description="The reference latitude of the center of the domain", + dimensional=True) @property def beta(self): @@ -461,8 +413,8 @@ def _create_insolation(self, values): d = ["spectral component "+str(pos+1)+" of the short-wave radiation of the atmosphere" for pos in range(dim)] - self.C = self.create_params_array(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True) + self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, + description=d, return_dimensional=True) def set_thetas(self, value, pos=None): """Function to define the spectral decomposition of the Newtonian cooling @@ -500,8 +452,8 @@ def _create_thetas(self, values): d = ["spectral component "+str(pos+1)+" of the temperature profile" for pos in range(dim)] - self.thetas = self.create_params_array(values, scale_object=self._scale_params, - description=d, return_dimensional=False, input_dimensional=False) + self.thetas = ArrayParameters(values, scale_object=self._scale_params, + description=d, return_dimensional=False, input_dimensional=False) class OceanicParams(Params): @@ -618,8 +570,8 @@ def _create_insolation(self, values): d = ["spectral component "+str(pos)+" of the short-wave radiation of the ocean" for pos in range(dim)] - self.C = self.create_params_array(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True) + self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, + description=d, return_dimensional=True) class GroundParams(Params): @@ -697,8 +649,8 @@ def _create_orography(self, values): d = ["spectral component "+str(pos+1)+" of the orography" for pos in range(dim)] - self.hk = self.create_params_array(values, scale_object=self._scale_params, - description=d, return_dimensional=False, input_dimensional=False) + self.hk = ArrayParameters(values, scale_object=self._scale_params, + description=d, return_dimensional=False, input_dimensional=False) class GroundTemperatureParams(Params): @@ -776,8 +728,8 @@ def _create_insolation(self, values): d = ["spectral component "+str(pos+1)+" of the short-wave radiation of the ground" for pos in range(dim)] - self.C = self.create_params_array(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True) + self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, + description=d, return_dimensional=True) class QgParams(Params): From 83879960a8c92bca5674599a29eb02c2dbf0804b Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Thu, 31 Aug 2023 17:53:12 +0200 Subject: [PATCH 056/143] Added some slight modifications to the previous commit --- qgs/params/parameter.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index c3a1362..b04cd24 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -48,6 +48,11 @@ >>> s.dimensional_values array([[1.0320000000000001e-05, 2.0640000000000002e-05], [3.096e-05, 4.1280000000000005e-05]], dtype=object) + >>> # you can also ask for the dimensional value of one particular value of the array + >>> s[0,0] + 0.1 + >>> s[0,0].dimensional_value + 1.0320000000000001e-05 Main class ---------- @@ -277,10 +282,10 @@ class ArrayParameters(np.ndarray): A scale parameters object to compute the conversion between dimensional and nondimensional value. `None` by default. If `None`, cannot transform between dimensional and nondimentional value. description: str or list(str) or array(str), optional - String or an iterable of string, describing the parameters. + String or an iterable of strings, describing the parameters. If an iterable, should have the same length or shape as `values`. symbol: ~sympy.core.symbol.Symbol or list(~sympy.core.symbol.Symbol) or ~numpy.ndarray(~sympy.core.symbol.Symbol), optional - A `Sympy`_ symbol or an iterable of, to represent the parameters in symbolic expressions. + A `Sympy`_ symbol or an iterable of symbols, to represent the parameters in symbolic expressions. If an iterable, should have the same length or shape as `values`. return_dimensional: bool, optional Defined if the value returned by the parameter is dimensional or not. Default to `False`. From e304a4798ece6c419a1f94fec47ffb69560c72d1 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 1 Sep 2023 14:40:59 +0200 Subject: [PATCH 057/143] Implemented symbols in Parameter and ArrayParameter --- model_test/test_aotensor_sym.py | 3 + qgs/params/params.py | 270 +++++++++++++++++++------------- 2 files changed, 163 insertions(+), 110 deletions(-) diff --git a/model_test/test_aotensor_sym.py b/model_test/test_aotensor_sym.py index 9acfa23..4cf5c9f 100644 --- a/model_test/test_aotensor_sym.py +++ b/model_test/test_aotensor_sym.py @@ -22,6 +22,7 @@ real_eps = np.finfo(np.float64).eps + class TestSymbolicAOTensor(TestBaseSymbolic): ''' Test class for the Linear Symbolic Tensor @@ -88,6 +89,7 @@ def numerical_outputs(self, output_func=None): for coo, val in zip(num_aotensor.tensor.coords.T, num_aotensor.tensor.data): _ip_string_format(tfunc, 'num_aotensor', coo, val) + def _ip_string_format(func, symbol, indices, value): if abs(value) >= real_eps: s = symbol @@ -96,5 +98,6 @@ def _ip_string_format(func, symbol, indices, value): s += " = % .5E" % value func(s) + if __name__ == "__main__": unittest.main() diff --git a/qgs/params/params.py b/qgs/params/params.py index d1738b8..1327660 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -50,7 +50,7 @@ from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis -import sympy as sy +from sympy import Symbol, simplify, ImmutableSparseMatrix # TODO: - store model version in a variable somewhere # - force or warn the user to define the aspect ratio n at parameter object instantiation @@ -226,8 +226,8 @@ def __init__(self, dic=None): self.scale = ScalingParameter(5.e6, units='[m]', description="characteristic space scale (L*pi)", dimensional=True) self.f0 = ScalingParameter(1.032e-4, units='[s^-1]', description="Coriolis parameter at the middle of the domain", - dimensional=True) - self.n = ScalingParameter(1.3e0, dimensional=False, description="aspect ratio (n = 2 L_y / L_x)") + dimensional=True, symbol=Symbol('f0')) + self.n = ScalingParameter(1.3e0, dimensional=False, description="aspect ratio (n = 2 L_y / L_x)", symbol=Symbol('n', positive=True)) self.rra = ScalingParameter(6370.e3, units='[m]', description="earth radius", dimensional=True) self.phi0_npi = ScalingParameter(0.25e0, dimensional=False, description="latitude expressed in fraction of pi") self.deltap = ScalingParameter(5.e4, units='[Pa]', description='pressure difference between the two atmospheric layers', @@ -244,7 +244,7 @@ def __init__(self, dic=None): def L(self): """Parameter: Typical length scale :math:`L` of the model, in meters [:math:`m`].""" return ScalingParameter(self.scale / np.pi, units=self.scale.units, description='Typical length scale L', - dimensional=True) + symbol=Symbol('L'), dimensional=True) @property def L_y(self): @@ -264,14 +264,14 @@ def phi0(self): """Parameter: The reference latitude :math:`\\phi_0` at the center of the domain, expressed in radians [:math:`rad`].""" return ScalingParameter(self.phi0_npi * np.pi, units='[rad]', description="The reference latitude of the center of the domain", - dimensional=True) + dimensional=True, symbol=Symbol('phi0')) @property def beta(self): """Parameter: The meridional gradient of the Coriolis parameter at :math:`\\phi_0`, expressed in [:math:`m^{-1} s^{-1}`]. """ return Parameter(self.L / self.rra * np.cos(self.phi0) / np.sin(self.phi0), input_dimensional=False, units='[m^-1][s^-1]', scale_object=self, - description="Meridional gradient of the Coriolis parameter at phi_0") + description="Meridional gradient of the Coriolis parameter at phi_0", symbol=Symbol('beta')) class AtmosphericParams(Params): @@ -304,11 +304,11 @@ def __init__(self, scale_params, dic=None): # Parameters for the atmosphere self.kd = Parameter(0.1, input_dimensional=False, scale_object=scale_params, units='[s^-1]', - description="atmosphere bottom friction coefficient") + description="atmosphere bottom friction coefficient", symbol=Symbol('k_d')) self.kdp = Parameter(0.01, input_dimensional=False, scale_object=scale_params, units='[s^-1]', - description="atmosphere internal friction coefficient") + description="atmosphere internal friction coefficient", symbol=Symbol('k_p')) self.sigma = Parameter(0.2e0, input_dimensional=False, scale_object=scale_params, units='[m^2][s^-2][Pa^-2]', - description="static stability of the atmosphere") + description="static stability of the atmosphere", symbol=Symbol('sigma')) self.set_params(dic) @@ -365,7 +365,7 @@ def __init__(self, scale_params, dic=None): self._scale_params = scale_params self.hd = Parameter(0.045, input_dimensional=False, units='[s]', scale_object=scale_params, - description="Newtonian cooling coefficient") + description="Newtonian cooling coefficient", symbol=Symbol('hd')) self.thetas = None # Radiative equilibrium mean temperature decomposition on the model's modes self.gamma = None @@ -377,7 +377,7 @@ def __init__(self, scale_params, dic=None): self.set_params(dic) - def set_insolation(self, value, pos=None): + def set_insolation(self, value, pos=None, dynamic_T=False): """Function to define the spectral decomposition of the constant short-wave radiation of the atmosphere (insolation) :math:`C_{{\\rm a}, i}` (:attr:`~.AtmosphericTemperatureParams.C`). @@ -388,22 +388,27 @@ def set_insolation(self, value, pos=None): If an iterable is provided, create a vector of spectral decomposition parameters corresponding to it. pos: int, optional Indicate in which component to set the `value`. + dynamic_T: bool, optional + Whether or not the dynamic temperature scheme is activated. """ # TODO: - check for the dimensionality of the arguments if isinstance(value, (float, int)) and pos is not None and self.C is not None: + offset = 1 + if dynamic_T: + offset = 0 self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params, - description="spectral component "+str(pos+1)+" of the short-wave radiation of the atmosphere", - return_dimensional=True) + description="spectral component "+str(pos+offset)+" of the short-wave radiation of the atmosphere", + return_dimensional=True, symbol=Symbol('C_a'+str(pos+offset))) elif hasattr(value, "__iter__"): - self._create_insolation(value) + self._create_insolation(value, dynamic_T) else: warnings.warn('A scalar value was provided, but without the `pos` argument indicating in which ' + 'component of the spectral decomposition to put it: Spectral decomposition unchanged !' + 'Please specify it or give a vector as `value`.') - def _create_insolation(self, values): + def _create_insolation(self, values, dynamic_T=False): if hasattr(values, "__iter__"): dim = len(values) @@ -411,10 +416,14 @@ def _create_insolation(self, values): dim = values values = dim * [0.] - d = ["spectral component "+str(pos+1)+" of the short-wave radiation of the atmosphere" for pos in range(dim)] + offset = 1 + if dynamic_T: + offset = 0 + d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the atmosphere" for pos in range(dim)] + sy = [Symbol('C_a'+str(pos+offset)) for pos in range(dim)] self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True) + description=d, return_dimensional=True, symbol=sy) def set_thetas(self, value, pos=None): """Function to define the spectral decomposition of the Newtonian cooling @@ -434,7 +443,7 @@ def set_thetas(self, value, pos=None): if isinstance(value, (float, int)) and pos is not None and self.thetas is not None: self.thetas[pos] = Parameter(value, scale_object=self._scale_params, description="spectral components "+str(pos+1)+" of the temperature profile", - return_dimensional=False, input_dimensional=False) + return_dimensional=False, input_dimensional=False, symbol=Symbol('thetas_'+str(pos+1))) elif hasattr(value, "__iter__"): self._create_thetas(value) else: @@ -451,9 +460,10 @@ def _create_thetas(self, values): values = dim * [0.] d = ["spectral component "+str(pos+1)+" of the temperature profile" for pos in range(dim)] + sy = [Symbol('thetas_'+str(pos+1)) for pos in range(dim)] self.thetas = ArrayParameters(values, scale_object=self._scale_params, - description=d, return_dimensional=False, input_dimensional=False) + description=d, return_dimensional=False, input_dimensional=False, symbol=sy) class OceanicParams(Params): @@ -486,13 +496,13 @@ def __init__(self, scale_params, dic=None): self._scale_params = scale_params self.gp = Parameter(3.1e-2, units='[m][s^-2]', return_dimensional=True, scale_object=scale_params, - description='reduced gravity') + description='reduced gravity', symbol=Symbol('g_p')) self.r = Parameter(1.e-8, units='[s^-1]', scale_object=scale_params, - description="frictional coefficient at the bottom of the ocean") + description="frictional coefficient at the bottom of the ocean", symbol=Symbol('r')) self.h = Parameter(5.e2, units='[m]', return_dimensional=True, scale_object=scale_params, - description="depth of the water layer of the ocean") + description="depth of the water layer of the ocean", symbol=Symbol('h')) self.d = Parameter(1.e-8, units='[s^-1]', scale_object=scale_params, - description="strength of the ocean-atmosphere mechanical coupling") + description="strength of the ocean-atmosphere mechanical coupling", symbol=Symbol('d')) self.set_params(dic) @@ -529,14 +539,14 @@ def __init__(self, scale_params, dic=None): self._scale_params = scale_params self.gamma = Parameter(2.e8, units='[J][m^-2][K^-1]', scale_object=scale_params, return_dimensional=True, - description='specific heat capacity of the ocean') + description='specific heat capacity of the ocean', symbol=Symbol('gamma_o')) self.C = None self.T0 = None self.set_params(dic) - def set_insolation(self, value, pos=None): + def set_insolation(self, value, pos=None, dynamic_T=False): """Function to define the spectral decomposition of the constant short-wave radiation of the ocean (insolation) :math:`C_{{\\rm o}, i}` (:attr:`~.OceanicTemperatureParams.C`). @@ -547,20 +557,25 @@ def set_insolation(self, value, pos=None): If an iterable is provided, create a vector of spectral decomposition parameters corresponding to it. pos: int, optional Indicate in which component to set the `value`. + dynamic_T: bool, optional + Whether or not the dynamic temperature scheme is activated. """ if isinstance(value, (float, int)) and pos is not None and self.C is not None: + offset = 1 + if dynamic_T: + offset = 0 self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params, - description="spectral component "+str(pos)+" of the short-wave radiation of the ocean", - return_dimensional=True) + description="spectral component "+str(pos+offset)+" of the short-wave radiation of the ocean", + return_dimensional=True, symbol=Symbol('C_go'+str(pos+offset))) elif hasattr(value, "__iter__"): - self._create_insolation(value) + self._create_insolation(value, dynamic_T) else: warnings.warn('A scalar value was provided, but without the `pos` argument indicating in which ' + 'component of the spectral decomposition to put it: Spectral decomposition unchanged !' + 'Please specify it or give a vector as `value`.') - def _create_insolation(self, values): + def _create_insolation(self, values, dynamic_T=False): if hasattr(values, "__iter__"): dim = len(values) @@ -568,10 +583,14 @@ def _create_insolation(self, values): dim = values values = dim * [0.] - d = ["spectral component "+str(pos)+" of the short-wave radiation of the ocean" for pos in range(dim)] + offset = 1 + if dynamic_T: + offset = 0 + d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the ocean" for pos in range(dim)] + sy = [Symbol('C_go'+str(pos+offset)) for pos in range(dim)] self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True) + description=d, return_dimensional=True, symbol=sy) class GroundParams(Params): @@ -631,7 +650,7 @@ def set_orography(self, value, pos=None, basis="atmospheric"): if isinstance(value, (float, int)) and pos is not None and self.hk is not None: self.hk[pos] = Parameter(value, scale_object=self._scale_params, description="spectral components "+str(pos+1)+" of the orography", - return_dimensional=False, input_dimensional=False) + return_dimensional=False, input_dimensional=False, symbol=Symbol('hk')) elif hasattr(value, "__iter__"): self._create_orography(value) else: @@ -685,14 +704,14 @@ def __init__(self, scale_params, dic=None): self._scale_params = scale_params self.gamma = Parameter(2.e8, units='[J][m^-2][K^-1]', scale_object=scale_params, return_dimensional=True, - description='specific heat capacity of the ground') + description='specific heat capacity of the ground', symbol=Symbol('gamma_g')) self.C = None self.T0 = None self.set_params(dic) - def set_insolation(self, value, pos=None): + def set_insolation(self, value, pos=None, dynamic_T=False): """Function to define the decomposition of the constant short-wave radiation of the ground (insolation) :math:`C_{{\\rm g}, i}` (:attr:`~.GroundTemperatureParams.C`). @@ -703,22 +722,27 @@ def set_insolation(self, value, pos=None): If an iterable is provided, create a vector of spectral decomposition parameters corresponding to it. pos: int, optional Indicate in which component to set the `value`. + dynamic_T: bool, optional + Whether or not the dynamic temperature scheme is activated. """ # TODO: - check for the dimensionality of the arguments if isinstance(value, (float, int)) and pos is not None and self.C is not None: + offset = 1 + if dynamic_T: + offset = 0 self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params, - description="spectral component "+str(pos+1)+" of the short-wave radiation of the ground", - return_dimensional=True) + description="spectral component "+str(pos+offset)+" of the short-wave radiation of the ground", + return_dimensional=True, symbol=Symbol('C_go'+str(pos+offset))) elif hasattr(value, "__iter__"): - self._create_insolation(value) + self._create_insolation(value, dynamic_T) else: warnings.warn('A scalar value was provided, but without the `pos` argument indicating in which ' + 'component of the spectral decomposition to put it: Spectral decomposition unchanged !' + 'Please specify it or give a vector as `value`.') - def _create_insolation(self, values): + def _create_insolation(self, values, dynamic_T=False): if hasattr(values, "__iter__"): dim = len(values) @@ -726,10 +750,14 @@ def _create_insolation(self, values): dim = values values = dim * [0.] - d = ["spectral component "+str(pos+1)+" of the short-wave radiation of the ground" for pos in range(dim)] + offset = 1 + if dynamic_T: + offset = 0 + d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the ground" for pos in range(dim)] + sy = [Symbol('C_go'+str(pos+offset)) for pos in range(dim)] self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True) + description=d, return_dimensional=True, symbol=sy) class QgParams(Params): @@ -825,51 +853,51 @@ class QgParams(Params): #//TODO: Should this dictionary be separated into three separate for atm, ocn, gnd? symbolic_params = { # Scale Parameters - 'L': sy.Symbol('L'), - 'fo': sy.Symbol('f0'), - 'beta': sy.Symbol('beta'), - 'n': sy.Symbol('n', positive=True), - 'rr': sy.Symbol('R'), - 'sb': sy.Symbol('sigma_b'), + 'L': Symbol('L'), + 'fo': Symbol('f0'), + 'beta': Symbol('beta'), + 'n': Symbol('n', positive=True), + 'rr': Symbol('R'), + 'sb': Symbol('sigma_b'), # Atmosphere Parameters - 'kd': sy.Symbol('k_d'), - 'kdp': sy.Symbol('k_p'), - 'sigma': sy.Symbol('sigma'), + 'kd': Symbol('k_d'), + 'kdp': Symbol('k_p'), + 'sigma': Symbol('sigma'), # Atmosphere Temp Parameters - 'hd': sy.Symbol('hd'), - 'theta': sy.Symbol('theta'), + 'hd': Symbol('hd'), + 'theta': Symbol('theta'), #//TODO: Need to work out what thetas should be 'thetas': None, - 'atm_gamma': sy.Symbol('gamma_a'), - 'atm_C_val': sy.Symbol('C_a'), + 'atm_gamma': Symbol('gamma_a'), + 'atm_C_val': Symbol('C_a'), 'atm_C': None, - 'eps': sy.Symbol('epsilon'), - 'atm_T0': sy.Symbol('T_a0'), - 'sc': sy.Symbol('sc'), - 'hlambda': sy.Symbol('lambda'), + 'eps': Symbol('epsilon'), + 'atm_T0': Symbol('T_a0'), + 'sc': Symbol('sc'), + 'hlambda': Symbol('lambda'), # Ground Parameters - 'hk_val': sy.Symbol('h_k'), + 'hk_val': Symbol('h_k'), 'hk': None, # Ground Temperature Parameters - 'gnd_gamma': sy.Symbol('gamma_g'), + 'gnd_gamma': Symbol('gamma_g'), # Ground/ocean Parameters - 'go_C_val': sy.Symbol('C_go'), + 'go_C_val': Symbol('C_go'), 'go_C': None, - 'go_T0': sy.Symbol('T_go0'), + 'go_T0': Symbol('T_go0'), # Ocean Parameters - 'gp': sy.Symbol('g_p'), - 'r': sy.Symbol('r'), - 'h': sy.Symbol('h'), - 'd': sy.Symbol('d'), + 'gp': Symbol('g_p'), + 'r': Symbol('r'), + 'h': Symbol('h'), + 'd': Symbol('d'), # Ocean Temperature Parameters - 'ocn_gamma': sy.Symbol('gamma_o'), + 'ocn_gamma': Symbol('gamma_o'), } @@ -948,9 +976,9 @@ def __init__(self, dic=None, scale_params=None, # Physical constants self.rr = Parameter(287.058e0, return_dimensional=True, units='[J][kg^-1][K^-1]', - scale_object=self.scale_params, description="gas constant of dry air") + scale_object=self.scale_params, description="gas constant of dry air", symbol=Symbol('R')) self.sb = Parameter(5.67e-8, return_dimensional=True, units='[J][m^-2][s^-1][K^-4]', - scale_object=self.scale_params, description="Stefan-Boltzmann constant") + scale_object=self.scale_params, description="Stefan-Boltzmann constant", symbol=Symbol('sigma_b')) self.set_params(dic) @@ -1257,8 +1285,8 @@ def symbolic_insolation_array(self, Cpa=None, Cpgo=None): atm_C_list[1] = self.symbolic_params['atm_C_val'] go_C_list[1] = self.symbolic_params['go_C_val'] - self.symbolic_params['atm_C'] = sy.matrices.immutable.ImmutableSparseMatrix(atm_C_list) - self.symbolic_params['go_C'] = sy.matrices.immutable.ImmutableSparseMatrix(go_C_list) + self.symbolic_params['atm_C'] = ImmutableSparseMatrix(atm_C_list) + self.symbolic_params['go_C'] = ImmutableSparseMatrix(go_C_list) def symbolic_orography_array(self, hk=None): """Set the array hk from given value, or the defulat symbols @@ -1275,7 +1303,7 @@ def symbolic_orography_array(self, hk=None): oro_list = [0] * self.nmod[0] oro_list[1] = self.symbolic_params['hk_val'] - self.symbolic_params['hk'] = sy.matrices.immutable.ImmutableSparseMatrix(oro_list) + self.symbolic_params['hk'] = ImmutableSparseMatrix(oro_list) @property def ndim(self): @@ -1398,7 +1426,7 @@ def atmospheric_basis(self, basis): self._atmospheric_basis = basis self._number_of_atmospheric_modes = len(basis.functions) if self.dynamic_T: - self._atmospheric_basis.functions.insert(0, sy.simplify("1")) + self._atmospheric_basis.functions.insert(0, simplify("1")) if self.ground_params is not None and self.ground_params.orographic_basis == "atmospheric": self.ground_params.set_orography(self._number_of_atmospheric_modes * [0.e0]) @@ -1421,7 +1449,7 @@ def oceanic_basis(self, basis): self._number_of_ground_modes = 0 self._number_of_oceanic_modes = len(basis) if self.dynamic_T: - self._oceanic_basis.functions.insert(0, sy.simplify("1")) + self._oceanic_basis.functions.insert(0, simplify("1")) if self.atemperature_params is not None: # disable the Newtonian cooling @@ -1430,35 +1458,40 @@ def oceanic_basis(self, basis): self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]', scale_object=self.scale_params, description='specific heat capacity of the atmosphere', - return_dimensional=True) + return_dimensional=True, symbol=Symbol('gamma_a')) if self.dynamic_T: - self.atemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0]) - self.atemperature_params.set_insolation(100.0, 0) - self.atemperature_params.set_insolation(100.0, 1) + self.atemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0], None, True) + self.atemperature_params.set_insolation(100.0, 0, True) + self.atemperature_params.set_insolation(100.0, 1, True) else: self.atemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.atemperature_params.set_insolation(100.0, 0) self.atemperature_params.T0 = Parameter(270.0, units='[K]', scale_object=self.scale_params, return_dimensional=True, - description="stationary solution for the 0-th order atmospheric temperature") + description="stationary solution for the 0-th order atmospheric temperature", + symbol=Symbol('T_a0')) self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False, - description="emissivity coefficient for the grey-body atmosphere") + description="emissivity coefficient for the grey-body atmosphere", + symbol=Symbol('epsilon')) self.atemperature_params.sc = Parameter(1., input_dimensional=False, - description="ratio of surface to atmosphere temperature") + description="ratio of surface to atmosphere temperature", + symbol=Symbol('sc')) self.atemperature_params.hlambda = Parameter(20.00, units='[W][m^-2][K^-1]', scale_object=self.scale_params, return_dimensional=True, - description="sensible+turbulent heat exchange between ocean/ground and atmosphere") + description="sensible+turbulent heat exchange between ocean/ground and atmosphere", + symbol=Symbol('lambda')) if self.gotemperature_params is not None: if self.dynamic_T: - self.gotemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0]) - self.gotemperature_params.set_insolation(350.0, 0) - self.gotemperature_params.set_insolation(350.0, 1) + self.gotemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0], None, True) + self.gotemperature_params.set_insolation(350.0, 0, True) + self.gotemperature_params.set_insolation(350.0, 1, True) else: self.gotemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.gotemperature_params.set_insolation(350.0, 0) self.gotemperature_params.T0 = Parameter(285.0, units='[K]', scale_object=self.scale_params, return_dimensional=True, - description="stationary solution for the 0-th order oceanic temperature") + description="stationary solution for the 0-th order oceanic temperature", + symbol=Symbol('T_go0')) # if setting an ocean, then disable the orography if self.ground_params is not None: self.ground_params.hk = None @@ -1478,7 +1511,7 @@ def ground_basis(self, basis): self._number_of_ground_modes = len(basis) self._number_of_oceanic_modes = 0 if self.dynamic_T: - self._ground_basis.functions.insert(0, sy.simplify("1")) + self._ground_basis.functions.insert(0, simplify("1")) if self.atemperature_params is not None: # disable the Newtonian cooling @@ -1487,24 +1520,28 @@ def ground_basis(self, basis): self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]', scale_object=self.scale_params, description='specific heat capacity of the atmosphere', - return_dimensional=True) + return_dimensional=True, symbol=Symbol('gamma_g')) if self.dynamic_T: - self.atemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0]) - self.atemperature_params.set_insolation(100.0, 0) - self.atemperature_params.set_insolation(100.0, 1) + self.atemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0], None, True) + self.atemperature_params.set_insolation(100.0, 0, True) + self.atemperature_params.set_insolation(100.0, 1, True) else: self.atemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.atemperature_params.set_insolation(100.0, 0) self.atemperature_params.T0 = Parameter(270.0, units='[K]', scale_object=self.scale_params, return_dimensional=True, - description="stationary solution for the 0-th order atmospheric temperature") + description="stationary solution for the 0-th order atmospheric temperature", + symbol=Symbol('T_a0')) self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False, - description="emissivity coefficient for the grey-body atmosphere") + description="emissivity coefficient for the grey-body atmosphere", + symbol=Symbol('epsilon')) self.atemperature_params.sc = Parameter(1., input_dimensional=False, - description="ratio of surface to atmosphere temperature") + description="ratio of surface to atmosphere temperature", + symbol=Symbol('sc')) self.atemperature_params.hlambda = Parameter(20.00, units='[W][m^-2][K^-1]', scale_object=self.scale_params, return_dimensional=True, - description="sensible+turbulent heat exchange between ocean/ground and atmosphere") + description="sensible+turbulent heat exchange between ocean/ground and atmosphere", + symbol=Symbol('lambda')) if self.gotemperature_params is not None: # if orography is disabled, enable it! @@ -1516,14 +1553,15 @@ def ground_basis(self, basis): self.ground_params.set_orography(self._number_of_ground_modes * [0.e0]) self.ground_params.set_orography(0.1, 1) if self.dynamic_T: - self.gotemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0]) - self.gotemperature_params.set_insolation(350.0, 0) - self.gotemperature_params.set_insolation(350.0, 1) + self.gotemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0], None, True) + self.gotemperature_params.set_insolation(350.0, 0, True) + self.gotemperature_params.set_insolation(350.0, 1, True) else: self.gotemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.gotemperature_params.set_insolation(350.0, 0) self.gotemperature_params.T0 = Parameter(285.0, units='[K]', scale_object=self.scale_params, return_dimensional=True, - description="stationary solution for the 0-th order oceanic temperature") + description="stationary solution for the 0-th order oceanic temperature", + symbol=Symbol('T_go0')) def set_atmospheric_modes(self, basis, auto=False): """Function to configure the atmospheric modes (basis functions) used to project the PDEs onto. @@ -1863,19 +1901,24 @@ def oblocks(self, value): self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]', scale_object=self.scale_params, description='specific heat capacity of the atmosphere', - return_dimensional=True) + return_dimensional=True, + symbol=Symbol('gamma_a')) self.atemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.atemperature_params.set_insolation(100.0, 0) self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False, - description="emissivity coefficient for the grey-body atmosphere") + description="emissivity coefficient for the grey-body atmosphere", + symbol=Symbol('epsilon')) self.atemperature_params.T0 = Parameter(270.0, units='[K]', scale_object=self.scale_params, return_dimensional=True, - description="stationary solution for the 0-th order atmospheric temperature") + description="stationary solution for the 0-th order atmospheric temperature", + symbol=Symbol('T_a0')) self.atemperature_params.sc = Parameter(1., input_dimensional=False, - description="ratio of surface to atmosphere temperature") + description="ratio of surface to atmosphere temperature", + symbol=Symbol('sc')) self.atemperature_params.hlambda = Parameter(20.00, units='[W][m^-2][K^-1]', scale_object=self.scale_params, return_dimensional=True, - description="sensible+turbulent heat exchange between ocean/ground and atmosphere") + description="sensible+turbulent heat exchange between ocean/ground and atmosphere", + symbol=Symbol('lambda')) if self.gotemperature_params is not None: self._number_of_ground_modes = 0 @@ -1883,7 +1926,8 @@ def oblocks(self, value): self.gotemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.gotemperature_params.set_insolation(350.0, 0) self.gotemperature_params.T0 = Parameter(285.0, units='[K]', scale_object=self.scale_params, return_dimensional=True, - description="stationary solution for the 0-th order oceanic temperature") + description="stationary solution for the 0-th order oceanic temperature", + symbol=Symbol('T_go0')) # if setting an ocean, then disable the orography if self.ground_params is not None: self.ground_params.hk = None @@ -1910,20 +1954,25 @@ def gblocks(self, value): self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]', scale_object=self.scale_params, description='specific heat capacity of the atmosphere', - return_dimensional=True) + return_dimensional=True, + symbol=Symbol('gamma_g')) self.atemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.atemperature_params.set_insolation(100.0, 0) self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False, - description="emissivity coefficient for the grey-body atmosphere") + description="emissivity coefficient for the grey-body atmosphere", + symbol=Symbol('epsilon')) self.atemperature_params.T0 = Parameter(270.0, units='[K]', scale_object=self.scale_params, return_dimensional=True, - description="stationary solution for the 0-th order atmospheric temperature") + description="stationary solution for the 0-th order atmospheric temperature", + symbol=Symbol('T_a0')) self.atemperature_params.sc = Parameter(1., input_dimensional=False, - description="ratio of surface to atmosphere temperature") + description="ratio of surface to atmosphere temperature", + symbol=Symbol('sc')) self.atemperature_params.hlambda = Parameter(20.00, units='[W][m^-2][K^-1]', scale_object=self.scale_params, return_dimensional=True, - description="sensible+turbulent heat exchange between ocean/ground and atmosphere") + description="sensible+turbulent heat exchange between ocean/ground and atmosphere", + symbol=Symbol('lambda')) if self.gotemperature_params is not None: gmod = 0 @@ -1943,7 +1992,8 @@ def gblocks(self, value): self.gotemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.gotemperature_params.set_insolation(350.0, 0) self.gotemperature_params.T0 = Parameter(285.0, units='[K]', scale_object=self.scale_params, return_dimensional=True, - description="stationary solution for the 0-th order oceanic temperature") + description="stationary solution for the 0-th order oceanic temperature", + symbol=Symbol('T_go0')) def _set_atmospheric_analytic_fourier_modes(self, nxmax, nymax, auto=False): From 60bf7d34c701240e4cfef9346fe9f70106f618b5 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 13 Sep 2023 14:23:47 +0200 Subject: [PATCH 058/143] Changed the name of parameters array Remove unneeded code in params.py --- qgs/params/parameter.py | 11 ++- qgs/params/params.py | 184 ++-------------------------------------- 2 files changed, 14 insertions(+), 181 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index b04cd24..c74ede8 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -9,7 +9,7 @@ -------- >>> from qgs.params.params import ScaleParams - >>> from qgs.params.parameter import Parameter, ArrayParameters + >>> from qgs.params.parameter import Parameter, ParametersArray >>> import numpy as np >>> # defining a scale object to help Parameter compute the nondimensionalization >>> sc = ScaleParams() @@ -39,7 +39,7 @@ False >>> # creating a parameters array initialized with a nondimensional values and returning >>> # nondimensional ones when called - >>> s = ArrayParameters(np.array([[0.1,0.2],[0.3,0.4]]), input_dimensional=False, scale_object=sc, units='[s^-1]', + >>> s = ParametersArray(np.array([[0.1,0.2],[0.3,0.4]]), input_dimensional=False, scale_object=sc, units='[s^-1]', ... description="atmosphere bottom friction coefficient") >>> s ArrayParameters([[0.1, 0.2], @@ -265,7 +265,7 @@ def _nondimensionalization(self): return self._conversion_factor(self._units, self._scale_object) -class ArrayParameters(np.ndarray): +class ParametersArray(np.ndarray): """Base class of model's array of parameters. Parameters @@ -368,6 +368,11 @@ def symbols(self): symbols[idx] = self[idx].symbol return symbols + @property + def sparsesymbols(self): + """To be implemented""" + pass + @property def input_dimensional(self): """bool: Indicate if the provided value is dimensional or not.""" diff --git a/qgs/params/params.py b/qgs/params/params.py index 1327660..ad56cf8 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -46,7 +46,7 @@ import warnings from abc import ABC -from qgs.params.parameter import Parameter, ScalingParameter, ArrayParameters +from qgs.params.parameter import Parameter, ScalingParameter, ParametersArray from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis @@ -422,7 +422,7 @@ def _create_insolation(self, values, dynamic_T=False): d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the atmosphere" for pos in range(dim)] sy = [Symbol('C_a'+str(pos+offset)) for pos in range(dim)] - self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, + self.C = ParametersArray(values, units='[W][m^-2]', scale_object=self._scale_params, description=d, return_dimensional=True, symbol=sy) def set_thetas(self, value, pos=None): @@ -462,7 +462,7 @@ def _create_thetas(self, values): d = ["spectral component "+str(pos+1)+" of the temperature profile" for pos in range(dim)] sy = [Symbol('thetas_'+str(pos+1)) for pos in range(dim)] - self.thetas = ArrayParameters(values, scale_object=self._scale_params, + self.thetas = ParametersArray(values, scale_object=self._scale_params, description=d, return_dimensional=False, input_dimensional=False, symbol=sy) @@ -589,7 +589,7 @@ def _create_insolation(self, values, dynamic_T=False): d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the ocean" for pos in range(dim)] sy = [Symbol('C_go'+str(pos+offset)) for pos in range(dim)] - self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, + self.C = ParametersArray(values, units='[W][m^-2]', scale_object=self._scale_params, description=d, return_dimensional=True, symbol=sy) @@ -668,7 +668,7 @@ def _create_orography(self, values): d = ["spectral component "+str(pos+1)+" of the orography" for pos in range(dim)] - self.hk = ArrayParameters(values, scale_object=self._scale_params, + self.hk = ParametersArray(values, scale_object=self._scale_params, description=d, return_dimensional=False, input_dimensional=False) @@ -756,7 +756,7 @@ def _create_insolation(self, values, dynamic_T=False): d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the ground" for pos in range(dim)] sy = [Symbol('C_go'+str(pos+offset)) for pos in range(dim)] - self.C = ArrayParameters(values, units='[W][m^-2]', scale_object=self._scale_params, + self.C = ParametersArray(values, units='[W][m^-2]', scale_object=self._scale_params, description=d, return_dimensional=True, symbol=sy) @@ -850,57 +850,6 @@ class QgParams(Params): """ _name = "General" - #//TODO: Should this dictionary be separated into three separate for atm, ocn, gnd? - symbolic_params = { - # Scale Parameters - 'L': Symbol('L'), - 'fo': Symbol('f0'), - 'beta': Symbol('beta'), - 'n': Symbol('n', positive=True), - 'rr': Symbol('R'), - 'sb': Symbol('sigma_b'), - - # Atmosphere Parameters - 'kd': Symbol('k_d'), - 'kdp': Symbol('k_p'), - 'sigma': Symbol('sigma'), - - # Atmosphere Temp Parameters - 'hd': Symbol('hd'), - 'theta': Symbol('theta'), - #//TODO: Need to work out what thetas should be - 'thetas': None, - 'atm_gamma': Symbol('gamma_a'), - 'atm_C_val': Symbol('C_a'), - 'atm_C': None, - 'eps': Symbol('epsilon'), - 'atm_T0': Symbol('T_a0'), - 'sc': Symbol('sc'), - 'hlambda': Symbol('lambda'), - - # Ground Parameters - 'hk_val': Symbol('h_k'), - 'hk': None, - - # Ground Temperature Parameters - 'gnd_gamma': Symbol('gamma_g'), - - # Ground/ocean Parameters - 'go_C_val': Symbol('C_go'), - 'go_C': None, - 'go_T0': Symbol('T_go0'), - - # Ocean Parameters - 'gp': Symbol('g_p'), - 'r': Symbol('r'), - 'h': Symbol('h'), - 'd': Symbol('d'), - - # Ocean Temperature Parameters - 'ocn_gamma': Symbol('gamma_o'), - - } - def __init__(self, dic=None, scale_params=None, atmospheric_params=True, atemperature_params=True, oceanic_params=None, otemperature_params=None, @@ -1255,56 +1204,6 @@ def print_params(self): print("=============================\n") print(s) - def symbolic_insolation_array(self, Cpa=None, Cpgo=None): - """Set the array Cpa and Cpgo from given value, or the default symbols - - It is assumed that the values given are either for Cpa, or Cpgo, there is no option to set both Cpa and Cpgo at the same time. - - Parameters - ---------- - Cpa: List(sympy Symbol, float) - A list of the sympy symbols, or floats, that will be converted to an ImmutableSparseNDimArray. - - Cpgo: List(sympy Symbol, float) - A list of the sympy symbols, or floats, that will be converted to an ImmutableSparseNDimArray. - - """ - if Cpa is not None: - atm_C_list = Cpa - elif Cpgo is not None: - go_C_list = Cpgo - - else: - atm_C_list = [0] * self.number_of_variables[1] - go_C_list = [0] * self.number_of_variables[1] - - atm_C_list[0] = self.symbolic_params['atm_C_val'] - go_C_list[0] = self.symbolic_params['go_C_val'] - - if self.dynamic_T: - atm_C_list[1] = self.symbolic_params['atm_C_val'] - go_C_list[1] = self.symbolic_params['go_C_val'] - - self.symbolic_params['atm_C'] = ImmutableSparseMatrix(atm_C_list) - self.symbolic_params['go_C'] = ImmutableSparseMatrix(go_C_list) - - def symbolic_orography_array(self, hk=None): - """Set the array hk from given value, or the defulat symbols - - Parameters - ---------- - hk: List(sympy Symbol, float) - A list of the sympy symbols, or floats, that will be converted to an ImmutableSparseNDimArray. - - """ - if hk is not None: - oro_list = hk - else: - oro_list = [0] * self.nmod[0] - oro_list[1] = self.symbolic_params['hk_val'] - - self.symbolic_params['hk'] = ImmutableSparseMatrix(oro_list) - @property def ndim(self): """int: Total number of variables of the model.""" @@ -2092,74 +1991,3 @@ def _set_ground_analytic_fourier_modes(self, nxmax=None, nymax=None, auto=True): for i in range(self.nmod[1]): self._ground_latex_var_string.append(r'delta T_{\rm g,' + str(i + 1) + "}") self._ground_var_string.append(r'delta_T_g_' + str(i + 1)) - - def _set_symbolic_parameters(self): - """ - Function to make a map between symbolic parameters and numberical values - """ - - #//TODO: THis function is a really lazy way of doing this, look into setting up the Parameter class with each symbol stored in the class - #//TODO: Need a better way to trigger this function, it cannot happen before the symbolic_qgtensor class is initiated I think? - self.symbol_to_value = dict() - - # Scale Parameters - if self.scale_params is not None: - self.symbol_to_value['L'] = (self.symbolic_params['L'], self.scale_params.L) - self.symbol_to_value['fo'] = (self.symbolic_params['fo'], self.scale_params.f0) - self.symbol_to_value['beta'] = (self.symbolic_params['beta'], self.scale_params.beta) - self.symbol_to_value['n'] = (self.symbolic_params['n'], self.scale_params.n) - self.symbol_to_value['rr'] = (self.symbolic_params['rr'], self.rr) - self.symbol_to_value['sb'] = (self.symbolic_params['sb'], self.sb) - - # Atmosphere Parameters - if self.atmospheric_params is not None: - self.symbol_to_value['kd'] = (self.symbolic_params['kd'], self.atmospheric_params.kd) - self.symbol_to_value['kdp'] = (self.symbolic_params['kdp'], self.atmospheric_params.kdp) - self.symbol_to_value['sigma'] = (self.symbolic_params['sigma'], self.atmospheric_params.sigma) - - - #//TODO: Fix the bogde on the index of the insolation - # Atmosphere Temp Parameters - if self.atemperature_params is not None: - self.symbol_to_value['hd'] = (self.symbolic_params['hd'], self.atemperature_params.hd) - self.symbol_to_value['theta'] = (self.symbolic_params['theta'], self.atemperature_params.thetas) - self.symbol_to_value['thetas'] = (self.symbolic_params['theta'], self.atemperature_params.thetas) - self.symbol_to_value['atm_gamma'] = (self.symbolic_params['atm_gamma'], self.atemperature_params.gamma) - self.symbol_to_value['atm_C_val'] = (self.symbolic_params['atm_C_val'], self.atemperature_params.C[0]) - self.symbol_to_value['atm_C'] = (self.symbolic_params['atm_C_val'], self.atemperature_params.C[0]) - self.symbol_to_value['eps'] = (self.symbolic_params['eps'], self.atemperature_params.eps) - self.symbol_to_value['atm_T0'] = (self.symbolic_params['atm_T0'], self.atemperature_params.T0) - self.symbol_to_value['sc'] = (self.symbolic_params['sc'], self.atemperature_params.sc) - self.symbol_to_value['hlambda'] = (self.symbolic_params['hlambda'], self.atemperature_params.hlambda) - - # Ground Parameters - if self.ground_params is not None: - self.symbol_to_value['hk_val'] = (self.symbolic_params['hk_val'], self.ground_params.hk) - #TODO The index here should not be hard coded - self.symbol_to_value['hk'] = (self.symbolic_params['hk_val'], self.ground_params.hk[1]) - - #//TODO: Fix the bogde on the index of the insolation - # Ground Temperature Parameters - if self.gotemperature_params is not None: - self.symbol_to_value['gnd_gamma'] = (self.symbolic_params['gnd_gamma'], self.gotemperature_params.gamma) - - # Ground/ocean Parameters - if self.gotemperature_params is not None: - #TODO The index here should not be hard coded - self.symbol_to_value['go_C_val'] = (self.symbolic_params['go_C_val'], self.gotemperature_params.C[0]) - - #TODO The index here should not be hard coded - self.symbol_to_value['go_C'] = (self.symbolic_params['go_C_val'], self.gotemperature_params.C[0]) - self.symbol_to_value['go_T0'] = (self.symbolic_params['go_T0'], self.gotemperature_params.T0) - - # Ocean Parameters - if self.oceanic_params is not None: - self.symbol_to_value['gp'] = (self.symbolic_params['gp'], self.oceanic_params.gp) - self.symbol_to_value['r'] = (self.symbolic_params['r'], self.oceanic_params.r) - self.symbol_to_value['h'] = (self.symbolic_params['h'], self.oceanic_params.h) - self.symbol_to_value['d'] = (self.symbolic_params['d'], self.oceanic_params.d) - - #//TODO: Fix the bogde on the index of the insolation - # Ocean Temperature Parameters - if self.gotemperature_params is not None: - self.symbol_to_value['ocn_gamma'] = (self.symbolic_params['ocn_gamma'], self.gotemperature_params.gamma) From 64836d7da439ab9496d5d41f9f310e1ce772fbb9 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 14 Sep 2023 15:53:55 +0200 Subject: [PATCH 059/143] Modifying pipeline to work with new parameters functionality - Not working --- qgs/inner_products/symbolic.py | 18 +-- qgs/params/params.py | 29 ++-- qgs/tensors/symbolic_qgtensor.py | 230 +++++++++++++++---------------- 3 files changed, 140 insertions(+), 137 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index c45a5c9..3a282e7 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -172,15 +172,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.return_symbolic = return_symbolic if return_symbolic: self.mk_subs = make_substitution - # _parallel_compute = _symbolic_compute if self.mk_subs: - self.subs = [(params.symbolic_params['n'], self.n)] + self.subs = [(self.n.symbol, self.n)] else: self.subs = None else: self.mk_subs = True - # _parallel_compute = _parallel_compute - self.subs = [(params.symbolic_params['n'], self.n)] + self.subs = [(self.n.symbol, self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() @@ -815,15 +813,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.return_symbolic = return_symbolic if return_symbolic: self.mk_subs = make_substitution - # _parallel_compute = _symbolic_compute if self.mk_subs: - self.subs = [(params.symbolic_params['n'], self.n)] + self.subs = [(self.n.symbol, self.n)] else: self.subs = None else: self.mk_subs = True - # _parallel_compute = _parallel_compute - self.subs = [(params.symbolic_params['n'], self.n)] + self.subs = [(self.n.symbol, self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() @@ -1300,15 +1296,13 @@ def __init__(self, params=None, stored=True, inner_product_definition=None, inte self.return_symbolic = return_symbolic if return_symbolic: self.mk_subs = make_substitution - # _parallel_compute = _symbolic_compute if self.mk_subs: - self.subs = [(params.symbolic_params['n'], self.n)] + self.subs = [(self.n.symbol, self.n)] else: self.subs = None else: self.mk_subs = True - # _parallel_compute = _parallel_compute - self.subs = [(params.symbolic_params['n'], self.n)] + self.subs = [(self.n.symbol, self.n)] if inner_product_definition is None: self.ip = StandardSymbolicInnerProductDefinition() diff --git a/qgs/params/params.py b/qgs/params/params.py index ad56cf8..4d1eedd 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -67,8 +67,6 @@ class Params(ABC): _name = "" - symbolic_params = dict() - def __init__(self, dic=None): self.set_params(dic) @@ -316,7 +314,7 @@ def __init__(self, scale_params, dic=None): def sig0(self): """Parameter: Static stability of the atmosphere divided by 2.""" return Parameter(self.sigma / 2, input_dimensional=False, scale_object=self._scale_params, units='[m^2][s^-2][Pa^-2]', - description="0.5 * static stability of the atmosphere") + description="0.5 * static stability of the atmosphere", symbol=self.sigma.symbol / 2) class AtmosphericTemperatureParams(Params): @@ -666,10 +664,11 @@ def _create_orography(self, values): dim = values values = dim * [0.] + d = ["spectral component "+str(pos+1)+" of the orography" for pos in range(dim)] self.hk = ParametersArray(values, scale_object=self._scale_params, - description=d, return_dimensional=False, input_dimensional=False) + description=d, return_dimensional=False, input_dimensional=False, symbol=values) class GroundTemperatureParams(Params): @@ -771,8 +770,6 @@ class QgParams(Params): Scale parameters instance. If `None`, create a new ScaleParams instance. Default to None. Default to `None`. - symbolic_params: dict(Symbolic Parameters), - A dictionary with the parameter names and symbolic variables. atmospheric_params: bool, None or AtmosphericParams, optional Atmospheric parameters instance. If 'True`, create a new AtmosphericParams instance. @@ -856,7 +853,8 @@ def __init__(self, dic=None, scale_params=None, ground_params=True, gtemperature_params=None, dynamic_T=False, T4=False): - Params.__init__(self, dic) + # Params.__init__(self, dic) + self.base_params = Params(dic) # General scale parameters object (Mandatory param block) if scale_params is None: @@ -923,7 +921,6 @@ def __init__(self, dic=None, scale_params=None, self.time_unit = 'days' # Physical constants - self.rr = Parameter(287.058e0, return_dimensional=True, units='[J][kg^-1][K^-1]', scale_object=self.scale_params, description="gas constant of dry air", symbol=Symbol('R')) self.sb = Parameter(5.67e-8, return_dimensional=True, units='[J][m^-2][s^-1][K^-4]', @@ -1147,10 +1144,24 @@ def set_params(self, dic): if dic is not None: for key, val in zip(dic.keys(), dic.values()): if key in self.__dict__.keys(): - self.__dict__[key] = val + if isinstance(self.__dict__[key], Parameter): + if isinstance(val, Parameter): + self.__dict__[key] = val + else: + d = self.__dict__[key].__dict__ + self.__dict__[key] = Parameter(val, + input_dimensional=d['_input_dimensional'], + units=d['_units'], + description=d['_description'], + scale_object=d['_scale_object'], + symbol=d['_symbol'], + return_dimensional=d['_return_dimensional']) + else: + self.__dict__[key] = val if 'scale_params' in self.__dict__.keys(): self.scale_params.set_params(dic) + if 'atmospheric_params' in self.__dict__.keys(): if self.atmospheric_params is not None: self.atmospheric_params.set_params(dic) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 2104dc3..327bf5c 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -12,6 +12,9 @@ import sympy as sy import pickle +from sympy.matrices.immutable import ImmutableSparseMatrix +from sympy.tensor.array import ImmutableSparseNDimArray + #//TODO: Check non stored IP version of this class SymbolicTensorLinear(object): @@ -60,16 +63,9 @@ def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_p self.ground_inner_products = ground_inner_products self.params = params - self.sym_params = self.params.symbolic_params - self.tensor = None self.jacobian_tensor = None - self.params.symbolic_insolation_array() - self.params.symbolic_orography_array() - - self.params._set_symbolic_parameters() - if not(self.params.dynamic_T): self.compute_tensor() @@ -151,79 +147,75 @@ def _deltaT_g(self, i): #//TODO: Im not happy with having these set of properties in two places, one for numerics and one for symbolic. This should be combined, or at least put in the parameter section somewhere. - @property - def sig0(self): - return self.sym_params['sigma'] / 2 - @property def LR(self): - return sy.sqrt(self.sym_params['gp'] * self.sym_params['h']) / self.sym_params['fo'] + return sy.sqrt(self.params.oceanic_params.gp.symbol * self.params.oceanic_params.h.symbol) / self.params.scale_params.f0.symbol @property def G(self): - return -self.sym_params['L'] ** 2 / self.LR ** 2 + return -self.params.scale_params.L.symbol ** 2 / self.LR ** 2 @property def Cpgo(self): - return self.sym_params['go_C'] / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) * self.sym_params['rr'] / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) + C = ImmutableSparseNDimArray(self.params.gotemperature_params.C.symbols) + return C / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) * self.params.rr.symbol / (self.params.scale_params.f0.symbol ** 2 * self.params.scale_params.L.symbol ** 2) @property def Lpgo(self): - return self.sym_params['hlambda'] / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + return self.params.atemperature_params.hlambda.symbol / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) @property def Cpa(self): - return self.sym_params['atm_C'] / (self.sym_params['atm_gamma'] * self.sym_params['fo']) * self.sym_params['rr'] / (self.sym_params['fo'] ** 2 * self.sym_params['L'] ** 2) / 2 + C = ImmutableSparseNDimArray(self.params.atemperature_params.C.symbols) + return C / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) * self.params.rr.symbol / (self.params.scale_params.f0.symbol ** 2 * self.params.scale_params.L.symbol ** 2) / 2 @property def Lpa(self): - return self.sym_params['hlambda'] / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + return self.params.atemperature_params.hlambda.symbol / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) @property def sbpgo(self): if self.params.gotemperature_params.T0 is None: None else: - return 4 * self.sym_params['sb'] * self.sym_params['go_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + return 4 * self.params.sb.symbol * self.params.gotemperature_params.T0.symbol ** 3 / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) @property def sbpa(self): if self.params.atemperature_params.T0 is None: return None else: - return 8 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['atm_T0'] ** 3 / (self.sym_params['gnd_gamma'] * self.sym_params['fo']) + return 8 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.atemperature_params.T0.symbol ** 3 / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) @property def LSBpgo(self): if self.params.gotemperature_params.T0 is None: None else: - return 2 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['go_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + return 2 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.gotemperature_params.T0.symbol ** 3 / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) @property def LSBpa(self): if self.params.atemperature_params.T0 is None: return None else: - return 8 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['atm_T0'] ** 3 / (self.sym_params['atm_gamma'] * self.sym_params['fo']) + return 8 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.atemperature_params.T0.symbol ** 3 / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) @property def T4sbpgo(self): - return self.sym_params['sb'] * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['gnd_gamma'] * self.sym_params['rr'] ** 3) + return self.params.sb.symbol * self.params.scale_params.L.symbol ** 6 * self.params.scale_params.f0.symbol ** 5 / (self.params.gotemperature_params.gamma.symbol * self.params.rr.symbol ** 3) @property def T4sbpa(self): - return 16 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['gnd_gamma'] * self.sym_params['rr'] ** 3) + return 16 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.scale_params.L.symbol ** 6 * self.params.scale_params.f0.symbol ** 5 / (self.params.gotemperature_params.gamma.symbol * self.params.rr.symbol ** 3) @property def T4LSBpgo(self): - return 0.5 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['atm_gamma'] * self.sym_params['rr'] ** 3) + return 0.5 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.scale_params.L.symbol ** 6 * self.params.scale_params.f0.symbol ** 5 / (self.params.atemperature_params.gamma.symbol * self.params.rr.symbol ** 3) @property def T4LSBpa(self): - return 16 * self.sym_params['eps'] * self.sym_params['sb'] * self.sym_params['L'] ** 6 * self.sym_params['fo'] ** 5 / (self.sym_params['atm_gamma'] * self.sym_params['rr'] ** 3) - - #//TODO: Do i need the scaling parameters? + return 16 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.scale_params.L.symbol ** 6 * self.params.scale_params.f0.symbol ** 5 / (self.params.atemperature_params.gamma.symbol * self.params.rr.symbol ** 3) def _compute_tensor_dicts(self): if self.params is None: @@ -234,10 +226,12 @@ def _compute_tensor_dicts(self): return None aips = self.atmospheric_inner_products + par = self.params - symbolic_params = self.sym_params atp = par.atemperature_params gp = par.ground_params + ap = par.atmospheric_params + nvar = par.number_of_variables bips = None @@ -265,16 +259,16 @@ def _compute_tensor_dicts(self): for j in range(offset, nvar[1]): a_inv[(i - offset, j - offset)] = aips.a(i, j) - a_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[0], nvar[0], a_inv) + a_inv = ImmutableSparseMatrix(nvar[0], nvar[0], a_inv) a_inv = a_inv.inverse() a_inv = a_inv.simplify() a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) + a_theta[(i, j)] = ap.sig0.symbol * aips.a(i, j) - aips.u(i, j) - a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) + a_theta = ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() a_theta = a_theta.simplify() @@ -284,7 +278,7 @@ def _compute_tensor_dicts(self): for i in range(nvar[3]): for j in range(nvar[3]): U_inv[(i, j)] = bips.U(i, j) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) + U_inv = ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) U_inv = U_inv.inverse() U_inv = U_inv.simplify() @@ -293,7 +287,7 @@ def _compute_tensor_dicts(self): for j in range(offset, nvar[3]): M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.G * bips.U(i, j) - M_psio = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) + M_psio = ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) M_psio = M_psio.inverse() M_psio = M_psio.simplify() @@ -302,7 +296,7 @@ def _compute_tensor_dicts(self): for i in range(nvar[2]): for j in range(nvar[2]): U_inv[(i, j)] = bips.U(i, j) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) + U_inv = ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) U_inv = U_inv.inverse() U_inv = U_inv.simplify() @@ -318,12 +312,16 @@ def _compute_tensor_dicts(self): if aips.stored and go: # psi_a part a_inv_mult_c = _symbolic_tensordot(a_inv, aips._c[offset:, offset:], axes=1) - if symbolic_params['hk'] is not None: - a_inv_mult_g = _symbolic_tensordot(a_inv, aips._g[offset:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_inv_mult_g, symbolic_params['hk'], axes=1) - else: - a_inv_mult_gh = _symbolic_tensordot(a_inv, aips._gh[offset:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_inv_mult_gh, symbolic_params['hk'], axes=1) + + if gp is not None: + hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbol) + + if gp.orographic_basis == "atmospheric": + a_inv_mult_g = _symbolic_tensordot(a_inv, aips._g[offset:, offset:, offset:], axes=1) + oro = _symbolic_tensordot(a_inv_mult_g, hk_sym_arr, axes=1) + else: + a_inv_mult_gh = _symbolic_tensordot(a_inv, aips._gh[offset:, offset:, offset:], axes=1) + oro = _symbolic_tensordot(a_inv_mult_gh, gp.hk.symbol, axes=1) a_inv_mult_b = _symbolic_tensordot(a_inv, aips._b[offset:, offset:, offset:], axes=1) @@ -333,16 +331,14 @@ def _compute_tensor_dicts(self): for i in range(nvar[0]): for j in range(nvar[0]): - jo = j + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -a_inv_mult_c[i, j] * symbolic_params['beta']) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -(symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -a_inv_mult_c[i, j] * par.scale_params.beta.symbol) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -(ap.kd.symbol * _kronecker_delta(i, j)) / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd.symbol * _kronecker_delta(i, j)) / 2) if gp is not None: # convert - if symbolic_params['hk'] is not None: - + if gp.hk is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -oro[i, j][0] / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j][0] / 2) @@ -353,15 +349,16 @@ def _compute_tensor_dicts(self): if ocean: for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * symbolic_params['kd'] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * ap.kd.symbol / 2) # theta_a part a_theta_mult_u = _symbolic_tensordot(a_theta, aips._u, axes=1) if self.Cpa is not None: val_Cpa = _symbolic_tensordot(a_theta_mult_u , self.Cpa, axes=1) - if symbolic_params['hd'] is not None and symbolic_params['thetas'] is not None: - val_thetas = _symbolic_tensordot(a_theta_mult_u, symbolic_params['thetas'], axes=1) # not perfect + if atp.hd is not None and atp.thetas is not None: + thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbol) + val_thetas = _symbolic_tensordot(a_theta_mult_u, thetas_sym_arr, axes=1) # not perfect a_theta_mult_a = _symbolic_tensordot(a_theta, aips._a[:, offset:], axes=1) a_theta_mult_c = _symbolic_tensordot(a_theta, aips._c[:, offset:], axes=1) @@ -370,10 +367,10 @@ def _compute_tensor_dicts(self): if gp is not None: if gp.orographic_basis == "atmospheric": - oro = _symbolic_tensordot(a_theta_mult_g, symbolic_params['hk'], axes=1) + oro = _symbolic_tensordot(a_theta_mult_g, hk_sym_arr, axes=1) else: a_theta_mult_gh = _symbolic_tensordot(a_theta, aips._gh[:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_theta_mult_gh, symbolic_params['hk'], axes=1) + oro = _symbolic_tensordot(a_theta_mult_gh, hk_sym_arr, axes=1) a_theta_mult_b = _symbolic_tensordot(a_theta, aips._b[:, offset:, offset:], axes=1) @@ -386,38 +383,37 @@ def _compute_tensor_dicts(self): for i in range(nvar[1]): if self.Cpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i][0]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i]) - if symbolic_params['hd'] is not None and atp.thetas is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_thetas[i][0] * symbolic_params['hd']) + if atp.hd is not None and atp.thetas is not None: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_thetas[i] * atp.hd.symbol) for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - val_2 = a_theta_mult_a[i, j] * symbolic_params['kd'] * self.sig0 / 2 - val_3 = a_theta_mult_a[i, j] * (symbolic_params['kd'] / 2 + 2 * symbolic_params['kdp']) * self.sig0 + val_2 = a_theta_mult_a[i, j] * ap.kd.symbol * ap.sig0.symbol / 2 + val_3 = a_theta_mult_a[i, j] * (ap.kd.symbol / 2 + 2 * ap.kdp.symbol) * ap.sig0.symbol sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val_2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -val_3) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -a_theta_mult_c[i, j] * symbolic_params['beta'] * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -a_theta_mult_c[i, j] * par.scale_params.beta.symbol * ap.sig0.symbol) if gp is not None: - if symbolic_params['hk'] is not None: - - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -self.sig0 * oro[i, j][0] / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro[i, j][0] / 2) + if gp.hk is not None: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -ap.sig0.symbol * oro[i, j][0] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro[i, j][0] / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - a_theta_mult_b[i, j, k] * self.sig0) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - a_theta_mult_b[i, j, k] * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - a_theta_mult_b[i, j, k] * ap.sig0.symbol) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - a_theta_mult_b[i, j, k] * ap.sig0.symbol) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), a_theta_mult_g[i, j, k]) for j in range(nvar[1]): if self.Lpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * symbolic_params['sc'] * self.Lpa) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.sc.symbol * self.Lpa) if self.LSBpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * self.LSBpa) @@ -426,7 +422,7 @@ def _compute_tensor_dicts(self): if ocean: for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), -a_theta_mult_d[i, j] * self.sig0 * symbolic_params['kd'] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), -a_theta_mult_d[i, j] * ap.sig0.symbol * ap.kd.symbol / 2) if self.Lpa is not None: for j in range(nvar[3]): @@ -452,13 +448,13 @@ def _compute_tensor_dicts(self): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), M_psio_mult_K[i, j] * symbolic_params['d']) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), -M_psio_mult_K[i, j] * symbolic_params['d']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), M_psio_mult_K[i, j] * par.oceanic_params.d.symbol) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), -M_psio_mult_K[i, j] * par.oceanic_params.d.symbol) for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_N[i, j] * symbolic_params['beta']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_N[i, j] * par.scale_params.beta.symbol) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_M[i, j] * (symbolic_params['r'] + symbolic_params['d'])) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_M[i, j] * (par.oceanic_params.r.symbol + par.oceanic_params.d.symbol)) for k in range(nvar[2]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio_mult_C[i, j, k]) @@ -470,10 +466,10 @@ def _compute_tensor_dicts(self): U_inv_mult_O = _symbolic_tensordot(U_inv, bips._O[:, offset:, offset:], axes=1) for i in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i][0]) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i]) for j in range(nvar[1]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * symbolic_params['sc'] * self.Lpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.Lpgo) if self.sbpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) @@ -492,10 +488,10 @@ def _compute_tensor_dicts(self): U_inv_mult_W = _symbolic_tensordot(U_inv, bips._W, axes=1) U_inv_mult_W_Cpgo = _symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) for i in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i][0]) # not perfect + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i]) for j in range(nvar[1]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * symbolic_params['sc'] * self.Lpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.Lpgo) if self.sbpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) @@ -514,21 +510,22 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.c(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - val * symbolic_params['beta']) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (symbolic_params['kd'] * _kronecker_delta(i, j)) / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - val * par.scale_params.beta.symbol) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - (ap.kd.symbol * _kronecker_delta(i, j)) / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd.symbol * _kronecker_delta(i, j)) / 2) if gp is not None: - if symbolic_params['hk'] is not None: + hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbol) + if gp.hk is not None: oro = 0 if gp.orographic_basis == "atmospheric": for jj in range(nvar[0]): for kk in range(nvar[0]): - oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * symbolic_params['kd'] + oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * ap.kd.symbol else: for jj in range(nvar[0]): for kk in range(nvar[0]): - oro += a_inv[i, jj] * aips.gh(offset + jj, j, offset + kk) * symbolic_params['hk_val'] + oro += a_inv[i, jj] * aips.gh(offset + jj, j, offset + kk) * hk_sym_arr[kk] sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - oro / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro / 2) @@ -545,7 +542,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.d(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), val * symbolic_params['kd'] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), val * ap.kd.symbol / 2) # theta_a part @@ -555,12 +552,13 @@ def _compute_tensor_dicts(self): for kk in range(nvar[1]): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), - a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk]) - if symbolic_params['hd'] is not None and self.thetas is not None: + if atp.hd is not None and atp.thetas is not None: + thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbol) val = 0 for jj in range(nvar[1]): for kk in range(nvar[1]): - val -= a_theta[i, jj] * aips.u(jj, kk) * self.thetas[kk] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val * symbolic_params['hd']) + val -= a_theta[i, jj] * aips.u(jj, kk) * thetas_sym_arr[kk] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val * atp.hd.symbol) for j in range(nvar[0]): @@ -569,35 +567,35 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.a(jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val * symbolic_params['kd'] * self.sig0 / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - val * (symbolic_params['kd'] / 2 - 2 * symbolic_params['kdp']) * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val * ap.kd.symbol * ap.sig0.symbol / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - val * (ap.kd.symbol / 2 - 2 * ap.kdp.symbol) * ap.sig0.symbol) val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.c(jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), val * symbolic_params['beta'] * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), val * par.scale_params.beta.symbol * ap.sig0.symbol) if gp is not None: - if symbolic_params['hk'] is not None: + if gp.hk is not None: oro = 0 if gp.orographic_basis == "atmospheric": for jj in range(nvar[1]): for kk in range(nvar[0]): - oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * symbolic_params['hk'][kk] + oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * gp.hk.symbol[kk] else: for jj in range(nvar[1]): for kk in range(nvar[0]): - oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * symbolic_params['hk'][kk] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - self.sig0 * oro / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), self.sig0 * oro / 2) + oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * gp.hk.symbol[kk] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - ap.sig0.symbol * oro / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.b(jj, jo, ko) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - val * self.sig0) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - val * self.sig0) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - val * ap.sig0.symbol) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - val * ap.sig0.symbol) val = 0 for jj in range(nvar[1]): @@ -611,12 +609,12 @@ def _compute_tensor_dicts(self): val += a_theta[i, jj] * aips.u(jj, j) if self.Lpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * symbolic_params['sc'] * self.Lpa) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.sc.symbol * self.Lpa) if self.LSBpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.LSBpa) - if symbolic_params['hd'] is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * symbolic_params['hd']) + if atp.hd is not None: + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.hd.symbol) if ocean: for j in range(nvar[2]): @@ -624,7 +622,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.d(jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * self.sig0 * symbolic_params['kd'] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * ap.sig0.symbol * ap.kd.symbol / 2) if self.Lpa is not None: for j in range(nvar[3]): @@ -652,7 +650,7 @@ def _compute_tensor_dicts(self): jo = j + offset # skipping the theta 0 variable if it exists for jj in range(nvar[2]): - val = M_psio[i, jj] * bips.K(offset + jj, jo) * symbolic_params['d'] + val = M_psio[i, jj] * bips.K(offset + jj, jo) * par.oceanic_params.d.symbol sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), val) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), - val) @@ -662,12 +660,12 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.N(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * symbolic_params['beta']) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * par.scale_params.beta.symbol) val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.M(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * (symbolic_params['r'] + symbolic_params['d'])) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * (par.oceanic_params.r.symbol + par.oceanic_params.d.symbol)) for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists @@ -684,7 +682,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips.W(jj, j) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * symbolic_params['sc'] * self.Lpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) if self.sbpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * self.sbpa) @@ -710,7 +708,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips.W(jj, j) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * symbolic_params['sc'] * self.Lpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) if self.sbpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.sbpa) @@ -747,8 +745,8 @@ def _set_symbolic_tensor(self): else: dims = (ndim + 1, ndim + 1, ndim + 1) - jacobian_tensor = sy.tensor.array.ImmutableSparseNDimArray(self.jac_dic.copy(), dims) - tensor = sy.tensor.array.ImmutableSparseNDimArray(self.tensor_dic.copy(), dims) + jacobian_tensor = ImmutableSparseNDimArray(self.jac_dic.copy(), dims) + tensor = ImmutableSparseNDimArray(self.tensor_dic.copy(), dims) self.jacobian_tensor = jacobian_tensor.applyfunc(sy.simplify) self.tensor = tensor.applyfunc(sy.simplify) @@ -1001,8 +999,8 @@ def _compute_stored_full_dict(self): a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) - a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) + a_theta[(i, j)] = par.atmospheric_parameters.sig0.symbol * aips.a(i, j) - aips.u(i, j) + a_theta = ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() if bips is not None: @@ -1011,13 +1009,13 @@ def _compute_stored_full_dict(self): for i in range(nvar[3]): for j in range(nvar[3]): U_inv[(i, j)] = bips.U(i, j) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) + U_inv = ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) U_inv = U_inv.inverse() else: for i in range(nvar[2]): for j in range(nvar[2]): U_inv[i, j] = bips.U(i, j) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) + U_inv = ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) U_inv = U_inv.inverse() ################# @@ -1130,9 +1128,9 @@ def _compute_non_stored_full_dict(self): a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) + a_theta[(i, j)] = ap.sig0.symbol * aips.a(i, j) - aips.u(i, j) - a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) + a_theta = ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() if bips is not None: @@ -1141,14 +1139,14 @@ def _compute_non_stored_full_dict(self): for i in range(nvar[3]): for j in range(nvar[3]): U_inv[i, j] = bips.U(i, j) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) + U_inv = ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) else: U_inv = dict() for i in range(nvar[2]): for j in range(nvar[2]): U_inv[(i, j)] = bips.U(i, j) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) + U_inv = ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) U_inv = U_inv.inverse() @@ -1328,9 +1326,9 @@ def _compute_non_stored_full_dict(self): a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[(i, j)] = self.sig0 * aips.a(i, j) - aips.u(i, j) + a_theta[(i, j)] = ap.sig0.symbol * aips.a(i, j) - aips.u(i, j) - a_theta = sy.matrices.immutable.ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) + a_theta = ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() if bips is not None: @@ -1339,14 +1337,14 @@ def _compute_non_stored_full_dict(self): for i in range(nvar[3]): for j in range(nvar[3]): U_inv[i, j] = bips.U(i, j) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) + U_inv = ImmutableSparseMatrix(nvar[3], nvar[3], U_inv) else: U_inv = dict() for i in range(nvar[2]): for j in range(nvar[2]): U_inv[(i, j)] = bips.U(i, j) - U_inv = sy.matrices.immutable.ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) + U_inv = ImmutableSparseMatrix(nvar[2], nvar[2], U_inv) U_inv = U_inv.inverse() From ae084ac037f7cbeebf5a609388cf31069b2337e3 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 15 Sep 2023 15:07:49 +0200 Subject: [PATCH 060/143] New symbolic pipeline - Linear version working --- ... Symbolic Output-Linear-ground test.ipynb} | 35 ++++- notebooks/Symbolic Output-Linear.ipynb | 73 ++++++++-- qgs/functions/symbolic_output.py | 93 +++++-------- qgs/params/params.py | 19 ++- qgs/tensors/symbolic_qgtensor.py | 130 ++++++++++++------ 5 files changed, 227 insertions(+), 123 deletions(-) rename notebooks/{Symbolic Output-Linear-groud test.ipynb => Symbolic Output-Linear-ground test.ipynb} (96%) diff --git a/notebooks/Symbolic Output-Linear-groud test.ipynb b/notebooks/Symbolic Output-Linear-ground test.ipynb similarity index 96% rename from notebooks/Symbolic Output-Linear-groud test.ipynb rename to notebooks/Symbolic Output-Linear-ground test.ipynb index d843030..4843919 100644 --- a/notebooks/Symbolic Output-Linear-groud test.ipynb +++ b/notebooks/Symbolic Output-Linear-ground test.ipynb @@ -171,6 +171,16 @@ "Calculating the functions and tensor" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "8287cf38", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.ground_params.hk[0].symbol" + ] + }, { "cell_type": "code", "execution_count": null, @@ -181,7 +191,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, = create_symbolic_equations(model_parameters, continuation_variables={'atm_C', 'atm_C_val', 'go_C', 'go_C_val'})" + "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.atemperature_params.eps], language='python')" ] }, { @@ -390,8 +400,8 @@ "metadata": {}, "outputs": [], "source": [ - "varx = 1\n", - "vary = 20\n", + "varx = 2\n", + "vary = 1\n", "plt.figure(figsize=(12, 10))\n", "\n", "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", @@ -427,7 +437,7 @@ "outputs": [], "source": [ "plt.figure(figsize=(12, 8))\n", - "t_s = 10000\n", + "t_s = 100000\n", "\n", "plt.plot(reference_time_num[:t_s] * model_parameters.dimensional_time, reference_traj_num[varx, :t_s])\n", "plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", @@ -440,6 +450,23 @@ "id": "76ecb077", "metadata": {}, "outputs": [], + "source": [ + "plt.figure(figsize=(12, 8))\n", + "t_s = 1000\n", + "\n", + "vars = [1, 2]\n", + "\n", + "plt.plot(reference_traj_num[vars, :-1].T)\n", + "# plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6d76cd4c", + "metadata": {}, + "outputs": [], "source": [] } ], diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index b26d313..5312125 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -58,31 +58,31 @@ "metadata": {}, "outputs": [], "source": [ - "model_parameters = QgParams({'rr': 287.e0, 'sb': 5.6e-8}, dynamic_T=False)\n", - "model_parameters.set_params({'kd': 0.04, 'kdp': 0.04, 'n': 1.5})" + "model_parameters = QgParams(dynamic_T=False)\n", + "# model_parameters.set_params({'rr':100, 'n': 1.8})" ] }, { "cell_type": "code", "execution_count": null, - "id": "a2a8e7c1", + "id": "35037d2e", "metadata": {}, "outputs": [], "source": [ - "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", - "# Mode truncation at the wavenumber 2 in the x and at the \n", - "# wavenumber 4 in the y spatial coordinates for the ocean\n", - "model_parameters.set_oceanic_basin_fourier_modes(2, 4, mode=\"symbolic\")" + "model_parameters.rr" ] }, { "cell_type": "code", "execution_count": null, - "id": "006f3c7d", + "id": "a2a8e7c1", "metadata": {}, "outputs": [], "source": [ - "model_parameters.symbolic_params" + "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", + "# Mode truncation at the wavenumber 2 in the x and at the \n", + "# wavenumber 4 in the y spatial coordinates for the ocean\n", + "model_parameters.set_oceanic_basin_fourier_modes(2, 4, mode=\"symbolic\")" ] }, { @@ -98,6 +98,47 @@ "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc4f22ef", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.atemperature_params.hlambda.symbol" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69183ec2", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.params.parameter import Parameter, ScalingParameter\n", + "from sympy import Symbol" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2840bb6", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.atmospheric_params.kd.symbol" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2fa987d2", + "metadata": {}, + "outputs": [], + "source": [ + "from sympy.tensor.array import ImmutableDenseNDimArray" + ] + }, { "cell_type": "code", "execution_count": null, @@ -142,6 +183,16 @@ "## Outputting model equations" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "311a854d", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, _parameter_substitutions" + ] + }, { "cell_type": "code", "execution_count": null, @@ -149,7 +200,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations, _sub_values" + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations" ] }, { @@ -170,7 +221,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables={'eps'}, language='python', return_symbolic_qgtensor=True)" + "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.oceanic_params.h], language='python', return_symbolic_qgtensor=True)" ] }, { diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index fec5174..82db852 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -3,10 +3,8 @@ import warnings from qgs.functions.symbolic_mul import symbolic_sparse_mult2, symbolic_sparse_mult3, symbolic_sparse_mult4, symbolic_sparse_mult5 - from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, GroundSymbolicInnerProducts - -from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT, SymbolicTensorT4 +from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT, SymbolicTensorT4, _parameter_substitutions import os @@ -29,7 +27,9 @@ '**': '^' } -def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables={}, language='python', return_inner_products=False, return_jacobian=False, return_symbolic_eqs=False, return_symbolic_qgtensor=False): +# TODO: this function isnt working with user input IPs, still calculated them from scratch + +def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables=list(), language='python', return_inner_products=False, return_jacobian=False, return_symbolic_eqs=False, return_symbolic_qgtensor=False): """ Function to output the raw symbolic functions of the qgs model. @@ -73,8 +73,12 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con if continuation_variables is None: make_ip_subs = False else: - if 'n' in continuation_variables: - make_ip_subs = False + for cv in continuation_variables: + try: + if params.scale_params.n == cv: + make_ip_subs = False + except: + pass else: make_ip_subs = True @@ -125,14 +129,14 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con xx.append(sy.Symbol('U_'+str(i))) if params.dynamic_T: - eq = symbolic_sparse_mult5(agotensor.tensor_dic, xx, xx, xx, xx) + eq = symbolic_sparse_mult5(agotensor.sub_tensor(continuation_variables=continuation_variables), xx, xx, xx, xx) if return_jacobian: - Deq = symbolic_sparse_mult4(agotensor.jac_dic, xx, xx, xx) + Deq = symbolic_sparse_mult4(agotensor.sub_tensor(agotensor.jac_dic, continuation_variables=continuation_variables), xx, xx, xx) else: - eq = symbolic_sparse_mult3(agotensor.tensor_dic, xx, xx) + eq = symbolic_sparse_mult3(agotensor.sub_tensor(continuation_variables=continuation_variables), xx, xx) if return_jacobian: - Deq = symbolic_sparse_mult2(agotensor.jac_dic, xx) + Deq = symbolic_sparse_mult2(agotensor.sub_tensor(agotensor.jac_dic, continuation_variables=continuation_variables), xx) eq_simplified = dict() Deq_simplified = dict() @@ -152,10 +156,10 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con if return_jacobian: Deq_simplified = Deq - funcs = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, free_variables=continuation_variables) + func = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, continuation_variables=continuation_variables) ret = list() - ret.append('\n'.join(funcs)) + ret.append('\n'.join(func)) if return_jacobian: ret.append(Deq_simplified) if return_inner_products: @@ -214,7 +218,7 @@ def translate_equations(equations, language='python'): return str_eq -def format_equations(equations, params, save_loc=None, language='python', free_variables={}, print_equations=False): +def format_equations(equations, params, save_loc=None, language='python', print_equations=False): ''' Function formats the equations, in the programming language specified, and saves the equations to the specified location. The variables in the equation are substituted if the model variable is input. @@ -250,8 +254,6 @@ def format_equations(equations, params, save_loc=None, language='python', free_v ''' equation_dict = dict() - sub_vals = _sub_values(params, free_variables) - # Substitute variable symbols vector_subs = dict() if language == 'python': @@ -273,13 +275,8 @@ def format_equations(equations, params, save_loc=None, language='python', free_v free_vars = set() for k in equations.keys(): eq = equations[k].subs(vector_subs) + eq = eq.evalf() - #substitute syntax - if sub_vals is not None: - eq = eq.subs(sub_vals) - eq = eq.evalf() - else: - eq = eq.simplify() for vars in eq.free_symbols: if vars not in vector_subs.values(): free_vars.add(vars) @@ -302,7 +299,7 @@ def format_equations(equations, params, save_loc=None, language='python', free_v else: return equation_dict, free_vars -def equation_as_function(equations, params, string_output=True, language='python', free_variables={}): +def equation_as_function(equations, params, string_output=True, language='python', continuation_variables=dict()): ''' Converts the symbolic equations to a function in string format in the language syntax specified, or a lambdified python function @@ -325,7 +322,7 @@ def equation_as_function(equations, params, string_output=True, language='python ''' - eq_dict, free_vars = format_equations(equations, params, language=language, free_variables=free_variables) + eq_dict, free_vars = format_equations(equations, params, language=language) f_output = list() if language == 'python': @@ -357,7 +354,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('function f(t, U, kwargs...)') f_output.append('\t#Tendency function of the qgs model') - f_output.append('\tU_out = similar(U)') + f_output.append('\tF = similar(U)') for v in free_vars: f_output.append('\t' + str(v) + " = kwargs['" + str(v) + "']") @@ -407,12 +404,12 @@ def equation_as_function(equations, params, string_output=True, language='python for n, eq in enumerate(eq_dict.values()): f_output.append('F['+str(n+1)+'] = ' + str(eq)) - #TODO !!!! Killing output as I have no confidence in the above code !!!! + #TODO !!!! Killing output as I have not tested the above code !!!! f_output = None return f_output -def create_auto_file(equations, params, free_variables): +def create_auto_file(equations, params, continuation_variables): ''' Creates the auto configuration file and the model file. Saves files to specified folder. @@ -423,7 +420,7 @@ def create_auto_file(equations, params, free_variables): Dictionary of the substituted symbolic model equations params: QGParams The parameters fully specifying the model configuration. - free_variables: Set or List or None + continuation_variables: Set or List or None Variables that are not substituted with numerical values. If None, no symbols are substituted ''' @@ -433,30 +430,30 @@ def create_auto_file(equations, params, free_variables): # User passes the equations, with the variables to leave as variables. # The existing model parameters are used to populate the auto file - # The variables given as `free_variables` remain in the equations. + # The variables given as `continuation_variables` remain in the equations. # There is a limit of 1-10 remian variables base_path = os.path.dirname(__file__) base_file = '.modelproto' base_config = '.cproto' - if (len(free_variables) < 1) or (len(free_variables) > 10): + if (len(continuation_variables) < 1) or (len(continuation_variables) > 10): ValueError("Too many variables for auto file") # Declare variables declare_var = list() - for i in free_variables: + for i in continuation_variables: declare_var.append('DOUBLE PRECISION ' + str(i)) # make list of parameters var_list = list() var_ini = list() - sub_vals = _sub_values(params, free_variables) + model_parameters = _parameter_substitutions(params, {}) - for i, fv in enumerate(free_variables): - temp_str = "PAR(" + str(i) + ") = " + str(fv) + for i, cv in enumerate(continuation_variables): + temp_str = "PAR(" + str(i) + ") = " + str(cv.symbol) - initial_value = "PAR(" + str(i) + ") = " + str(sub_vals[fv]) + " Variable: " + str(fv) + initial_value = "PAR(" + str(i) + ") = " + str(cv) + " Variable: " + str(cv.symbol) var_list.append(temp_str) var_ini.append(initial_value) @@ -497,7 +494,7 @@ def create_auto_file(equations, params, free_variables): auto_config = list() for ln in lines: if '! PARAMETERS' in ln: - auto_config.append('parnames = ' + str({i+1: v for i, v in enumerate(free_variables)})) + auto_config.append('parnames = ' + str({i+1: v for i, v in enumerate(continuation_variables)})) elif '! VARIABLES' in ln: auto_config.append('unames = ' + str(_variable_names(params))) @@ -506,7 +503,7 @@ def create_auto_file(equations, params, free_variables): auto_config.append('NDIM = ' + str(params.ndim)) elif '! CONTINUATION ORDER' in ln: - auto_config.append('ICP = ' + str(list(free_variables))) + auto_config.append('ICP = ' + str(list(continuation_variables))) #TODO: Need to include bounds on continuation parameters @@ -516,30 +513,6 @@ def create_auto_file(equations, params, free_variables): print('\n'.join(auto_config)) return equations - -def _sub_values(params, free_variables): - # Substitute variables - if isinstance(free_variables, (set, list, dict)): - # make a dictionary of variables to substitute from parameters - - sub_vals = dict() - for key in params.symbol_to_value.keys(): - - # Sympy 1.12 cannot have subs contain None - if params.symbol_to_value[key][1] is not None: - if len(free_variables) == 0: - sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] - else: - if key not in free_variables: - sub_vals[params.symbol_to_value[key][0]] = params.symbol_to_value[key][1] - - elif free_variables is None: - sub_vals = None - - else: - raise ValueError("Incorrect type for substitution, needs to be set, list, or dict of strings, not: " + str(type(free_variables))) - - return sub_vals def _split_equations(eq_dict, f_output, line_len=80): ''' diff --git a/qgs/params/params.py b/qgs/params/params.py index 4d1eedd..1d4e1f4 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -853,8 +853,7 @@ def __init__(self, dic=None, scale_params=None, ground_params=True, gtemperature_params=None, dynamic_T=False, T4=False): - # Params.__init__(self, dic) - self.base_params = Params(dic) + Params.__init__(self, dic) # General scale parameters object (Mandatory param block) if scale_params is None: @@ -1177,14 +1176,20 @@ def set_params(self, dic): if 'ground_params' in self.__dict__.keys(): if self.ground_params is not None: self.ground_params.set_params(dic) - - if 'otemperature_params' in self.__dict__.keys(): + + #TODO: Needs checking! + + if 'gotemperature_params' in self.__dict__.keys(): if self.gotemperature_params is not None: self.gotemperature_params.set_params(dic) - if 'gtemperature_params' in self.__dict__.keys(): - if self.gotemperature_params is not None: - self.gotemperature_params.set_params(dic) + # if 'otemperature_params' in self.__dict__.keys(): + # if self.gotemperature_params is not None: + # self.gotemperature_params.set_params(dic) + + # if 'gtemperature_params' in self.__dict__.keys(): + # if self.gotemperature_params is not None: + # self.gotemperature_params.set_params(dic) def print_params(self): """Print all the parameters in the container.""" diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 327bf5c..9bb5c73 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -6,6 +6,7 @@ """ from qgs.functions.symbolic_mul import _add_to_dict, _symbolic_tensordot +from qgs.params.params import Parameter, ScalingParameter, ParametersArray import numpy as np import sparse as sp @@ -314,14 +315,14 @@ def _compute_tensor_dicts(self): a_inv_mult_c = _symbolic_tensordot(a_inv, aips._c[offset:, offset:], axes=1) if gp is not None: - hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbol) + hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbols) if gp.orographic_basis == "atmospheric": a_inv_mult_g = _symbolic_tensordot(a_inv, aips._g[offset:, offset:, offset:], axes=1) oro = _symbolic_tensordot(a_inv_mult_g, hk_sym_arr, axes=1) else: a_inv_mult_gh = _symbolic_tensordot(a_inv, aips._gh[offset:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_inv_mult_gh, gp.hk.symbol, axes=1) + oro = _symbolic_tensordot(a_inv_mult_gh, hk_sym_arr, axes=1) a_inv_mult_b = _symbolic_tensordot(a_inv, aips._b[offset:, offset:, offset:], axes=1) @@ -339,8 +340,8 @@ def _compute_tensor_dicts(self): if gp is not None: # convert if gp.hk is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -oro[i, j][0] / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j][0] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -oro[i, j] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j] / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists @@ -401,8 +402,8 @@ def _compute_tensor_dicts(self): if gp is not None: if gp.hk is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -ap.sig0.symbol * oro[i, j][0] / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro[i, j][0] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -ap.sig0.symbol * oro[i, j] / 2) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro[i, j] / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists @@ -515,7 +516,7 @@ def _compute_tensor_dicts(self): sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd.symbol * _kronecker_delta(i, j)) / 2) if gp is not None: - hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbol) + hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbols) if gp.hk is not None: oro = 0 if gp.orographic_basis == "atmospheric": @@ -581,11 +582,11 @@ def _compute_tensor_dicts(self): if gp.orographic_basis == "atmospheric": for jj in range(nvar[1]): for kk in range(nvar[0]): - oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * gp.hk.symbol[kk] + oro += a_theta[i, jj] * aips.g(jj, jo, offset + kk) * gp.hk[kk].symbol else: for jj in range(nvar[1]): for kk in range(nvar[0]): - oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * gp.hk.symbol[kk] + oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * gp.hk[kk].symbol sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - ap.sig0.symbol * oro / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro / 2) @@ -790,7 +791,7 @@ def simplify_dict(dic): dic_upp = _add_to_dict(dic_upp, new_key, dic[key]) return dic_upp - + def save_to_file(self, filename, **kwargs): """Function to save the tensor object to a file with the :mod:`pickle` module. @@ -805,7 +806,7 @@ def save_to_file(self, filename, **kwargs): pickle.dump(self.__dict__, f, **kwargs) f.close() - def sub_tensor(self, tensor=None, dict_opp=True, remain_variables=dict()): + def sub_tensor(self, tensor=None, continuation_variables=list()): """ Uses sympy substitution to convert the symbolic tensor or a symbolic dictionary to a numerical one. @@ -813,9 +814,8 @@ def sub_tensor(self, tensor=None, dict_opp=True, remain_variables=dict()): ---------- tensor: dict, sympy array - dict_opp: Boolian, if True, uses the stored tensor_dic object as the input - - remain_variables: dict of variables not to substitute + continuation_variables: Parameter, ScalingParameter + iterable of variables not to substitute if None all variables are substituted. This variable is the opposite of 'variables' Returns @@ -824,41 +824,26 @@ def sub_tensor(self, tensor=None, dict_opp=True, remain_variables=dict()): Dictionary of the substituted tensor of the model tendencies, with coordinates and value """ - symbol_to_number_map = list() - variables_not_found = list() - - for key in self.sym_params.keys(): - if key not in remain_variables: - try: - # Sympy 1.12 cannot have subs contain None - if self.params.symbol_to_value[key][1] is not None: - symbol_to_number_map.append(self.params.symbol_to_value[key]) - except: - variables_not_found.append(key) + param_subs = _parameter_substitutions(self.params, continuation_variables) - if isinstance(tensor, dict): - ten_out = dict() - for key in tensor.keys(): - val = tensor[key].subs(symbol_to_number_map) - try: - ten_out[key] = float(val) - except: - ten_out[key] = val + if tensor is None: + ten = self.tensor_dic + else: + ten = tensor - elif dict_opp: + if isinstance(ten, dict): ten_out = dict() - for key in self.tensor_dic.keys(): - val = self.tensor_dic[key].subs(symbol_to_number_map) + for key in ten.keys(): + val = ten[key].subs(param_subs) try: ten_out[key] = float(val) except: ten_out[key] = val else: - if tensor is not None: - ten_out = tensor.subs(symbol_to_number_map) - else: - ten_out = self.tensor.subs(symbol_to_number_map) + #Assuming the tensor is a sympy tensor + ten_out = ten.subs(param_subs) + return ten_out @@ -1481,7 +1466,70 @@ def _shift_dict_keys(dic, shift): new_key = key + shift shifted_dic[new_key] = dic[key] - return shifted_dic + return shifted_dic + +def _parameter_substitutions(params, continuation_varaibles): + + subs = _parameter_values(params) + + if 'scale_params' in params.__dict__.keys(): + subs.update(_parameter_values(params.scale_params)) + + # TODO: Is there a better way to do this which is dynamic, the __dict__ method doesnt include the properties? + # Manually add properties from class + subs[params.scale_params.L.symbol] = params.scale_params.L + subs[params.scale_params.beta.symbol] = params.scale_params.beta + + if 'atmospheric_params' in params.__dict__.keys(): + if params.atmospheric_params is not None: + subs.update(_parameter_values(params.atmospheric_params)) + + if 'atemperature_params' in params.__dict__.keys(): + if params.atemperature_params is not None: + subs.update(_parameter_values(params.atemperature_params)) + + if 'oceanic_params' in params.__dict__.keys(): + if params.oceanic_params is not None: + subs.update(_parameter_values(params.oceanic_params)) + + if 'ground_params' in params.__dict__.keys(): + if params.ground_params is not None: + subs.update(_parameter_values(params.ground_params)) + + if 'gotemperature_params' in params.__dict__.keys(): + if params.gotemperature_params is not None: + subs.update(_parameter_values(params.gotemperature_params)) + + # Remove variables in continuation variables + for cv in continuation_varaibles: + if isinstance(cv, ParametersArray): + for cv_i in cv.symbols: + subs.pop(cv_i) + else: + subs.pop(cv.symbol) + + return subs + +def _parameter_values(pars): + """ + Function takes a parameter class and produces a dictionary of the symbol and the corrisponding numerical value + """ + + subs = dict() + for val in pars.__dict__.values(): + if isinstance(val, Parameter): + if val.symbol is not None: + subs[val.symbol] = val + + if isinstance(val, ScalingParameter): + if val.symbol is not None: + subs[val.symbol] = val + + if isinstance(val, ParametersArray): + for v in val: + if v.symbol is not None or v.symbol != 0: + subs[v.symbol] = v + return subs if __name__ == "__main__": dic = dict() From 935c412b22b899cfc09042a270ebbafc41f6ed09 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 15 Sep 2023 16:03:43 +0200 Subject: [PATCH 061/143] Symbolic pipeline - dyn_T working - all tests passing --- notebooks/Symbolic Output-Linear.ipynb | 46 +-- notebooks/Symbolic Output-dynamic_T.ipynb | 343 +++++++++++++++++++++- qgs/functions/symbolic_output.py | 11 +- qgs/tensors/symbolic_qgtensor.py | 9 +- 4 files changed, 339 insertions(+), 70 deletions(-) diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 5312125..9b7d56c 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -98,16 +98,6 @@ "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "bc4f22ef", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.atemperature_params.hlambda.symbol" - ] - }, { "cell_type": "code", "execution_count": null, @@ -119,16 +109,6 @@ "from sympy import Symbol" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "b2840bb6", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.atmospheric_params.kd.symbol" - ] - }, { "cell_type": "code", "execution_count": null, @@ -139,30 +119,6 @@ "from sympy.tensor.array import ImmutableDenseNDimArray" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "ea85360f", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "atm_loaded = AtmosphericSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True) \n", - "ocn_loaded = OceanicSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "196d5440", - "metadata": {}, - "outputs": [], - "source": [ - "# atm_loaded.load_from_file('temp_atm_sym_lin.ip')\n", - "# ocn_loaded.load_from_file('temp_ocn_sym_lin.ip')" - ] - }, { "cell_type": "code", "execution_count": null, @@ -221,7 +177,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.oceanic_params.h], language='python', return_symbolic_qgtensor=True)" + " funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.atemperature_params.eps], language='python', return_symbolic_qgtensor=True)" ] }, { diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index da5f4d6..e96c57a 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -71,16 +71,6 @@ "model_parameters.set_oceanic_basin_fourier_modes(2, 4, mode=\"symbolic\")" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "006f3c7d", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.symbolic_params" - ] - }, { "cell_type": "code", "execution_count": null, @@ -158,7 +148,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables={}, return_symbolic_qgtensor=True, language='python')" + "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[], return_symbolic_qgtensor=True, language='python')" ] }, { @@ -171,6 +161,154 @@ "print(funcs)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6d2afea", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "ten.print_tensor(ten_num.tensor.todense())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27664807", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.functions.symbolic_mul import symbolic_sparse_mult5\n", + "from qgs.functions.symbolic_output import equation_as_function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3a76194", + "metadata": {}, + "outputs": [], + "source": [ + "xx = list()\n", + "xx.append(1)\n", + "for i in range(1, 39):\n", + " xx.append(sy.Symbol('U[' + str(i-1) + ']'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f044536", + "metadata": {}, + "outputs": [], + "source": [ + "fun_out = symbolic_sparse_mult5(ten.tensor_dic, xx, xx, xx, xx)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "587e2a50", + "metadata": {}, + "outputs": [], + "source": [ + "test_fun = '\\n'.join(equation_as_function(fun_out, model_parameters))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58bfe429", + "metadata": {}, + "outputs": [], + "source": [ + "print(test_fun)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c6f526dc", + "metadata": {}, + "outputs": [], + "source": [ + "def arr_to_dict(arr):\n", + " x, y, z, w, s = arr.shape\n", + " output = dict()\n", + " for i in range(x):\n", + " for j in range(y):\n", + " for k in range(z):\n", + " for l in range(w):\n", + " for m in range(s):\n", + " if arr[i, j, k, l, m] != 0:\n", + " output[(i, j, k, l, m)] = arr[i, j, k, l, m]\n", + " return output" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25e91755", + "metadata": {}, + "outputs": [], + "source": [ + "ten_num_dic = arr_to_dict(ten_num.tensor.todense())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d899609", + "metadata": {}, + "outputs": [], + "source": [ + "ten_dic = ten.sub_tensor()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d1f7603", + "metadata": {}, + "outputs": [], + "source": [ + "ten_dic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "093b86d5", + "metadata": {}, + "outputs": [], + "source": [ + "ten_num_dic" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a500d10", + "metadata": {}, + "outputs": [], + "source": [ + "for k in ten_num_dic.keys():\n", + " if ten_dic[k] - ten_num_dic[k] != 0:\n", + " print(k, (ten_dic[k] - ten_num_dic[k]) / ten_num_dic[k])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9001d905", + "metadata": {}, + "outputs": [], + "source": [ + "test_eq = symbolic_sparse_mult5(ten_num_dic, xx, xx, xx, xx)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -308,6 +446,68 @@ "\treturn F" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b8e33b0", + "metadata": {}, + "outputs": [], + "source": [ + "@njit\n", + "def f_test(t, U):\n", + "\t#Tendency function of the qgs model\n", + "\tF = np.empty_like(U)\n", + "\tF[0] = -0.0145*U[0] + 0.0145*U[11] + 0.0505574149661188*U[22] + 0.0734121368001177*U[24]\n", + "\tF[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[11]*U[13] + 0.0145*U[12] - 1.9945469157942*U[14]*U[16] - 2.59615384615385*U[15]*U[18] + 2.59615384615385*U[16]*U[17] - 0.0145*U[1] - 0.00295864958311857*U[21] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", + "\tF[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[11]*U[12] + 0.0145*U[13] + 1.9945469157942*U[14]*U[15] + 2.59615384615385*U[15]*U[17] + 2.59615384615385*U[16]*U[18] - 0.115315741885204*U[1] + 0.0145*U[25] - 0.0145*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", + "\tF[3] = 2.16075915877706*U[12]*U[16] - 2.16075915877706*U[13]*U[15] + 0.0145*U[14] + 4.32151831755411*U[17]*U[20] - 4.32151831755411*U[18]*U[19] + 2.16075915877706*U[1]*U[5] - 0.00216427290094687*U[21] + 0.0238416302768307*U[23] - 2.16075915877706*U[2]*U[4] - 0.0145*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", + "\tF[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[11]*U[16] + 2.43*U[12]*U[18] + 0.345721465404329*U[13]*U[14] - 2.43*U[13]*U[17] + 0.0145*U[15] + 2.43*U[1]*U[7] - 0.00449241352700723*U[22] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.0145*U[4] + 0.059964185780306*U[5]\n", + "\tF[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[11]*U[15] - 0.345721465404329*U[12]*U[14] - 2.43*U[12]*U[17] - 2.43*U[13]*U[18] + 0.0145*U[16] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.0145*U[26] - 2.43*U[2]*U[7] - 0.059964185780306*U[4] - 0.0145*U[5]\n", + "\tF[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[11]*U[18] + 0.675*U[12]*U[16] + 0.675*U[13]*U[15] - 5.18582198106493*U[14]*U[20] + 0.0145*U[17] + 0.675*U[1]*U[5] - 0.000192312222902707*U[21] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.0145*U[6] + 0.0749552322253824*U[7]\n", + "\tF[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[11]*U[17] - 0.675*U[12]*U[15] + 0.675*U[13]*U[16] + 5.18582198106493*U[14]*U[19] + 0.0145*U[18] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.0145*U[7]\n", + "\tF[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[11]*U[20] - 2.6593958877256*U[14]*U[18] + 0.0145*U[19] - 0.000431962839135312*U[22] - 2.6593958877256*U[3]*U[7] - 0.0145*U[8] + 0.0576578709426019*U[9]\n", + "\tF[9] = 2.6593958877256*U[0]*U[8] + 2.6593958877256*U[11]*U[19] + 2.6593958877256*U[14]*U[17] + 0.0145*U[20] + 2.6593958877256*U[3]*U[6] - 0.0576578709426019*U[8] - 0.0145*U[9]\n", + "\tF[10] = -0.000510753549879668*U[10]**4 - 0.014593023255814*U[10] - 0.00183644645351737*U[21] - 0.00374635076517544*U[23] + 1.59610484337396e-5*U[29]**4 + 5.17501542233338e-5*U[29]**3*U[30] + 1.72500514077779e-5*U[29]**3*U[32] + 0.00729651162790698*U[29] + 0.00591432955679693*U[30] + 0.00197144318559898*U[32] + 0.000531003677198668\n", + "\tF[11] = 0.00131818181818182*U[0] - 0.00185728563592607*U[10]**3*U[11] - 0.0198572938689218*U[11] - 1.63693875664928*U[12]*U[2] + 1.63693875664928*U[13]*U[1] - 1.30955100531943*U[15]*U[5] + 1.30955100531943*U[16]*U[4] - 3.27387751329857*U[17]*U[7] + 3.27387751329857*U[18]*U[6] - 2.61910201063885*U[19]*U[9] + 2.61910201063885*U[20]*U[8] - 0.00459612863328353*U[22] - 0.00667383061819252*U[24] + 4.43550120954775e-5*U[29]**3*U[31] + 1.7742004838191e-5*U[29]**3*U[33] + 0.00506916671004016*U[31] + 0.00202766668401607*U[33] + 0.000482730615635153\n", + "\tF[12] = -1.6647358298754*U[0]*U[13] - 0.0015418975090707*U[10]**3*U[12] + 1.05320021890077*U[11]*U[2] - 0.0287966213251426*U[12] + 0.0282849932925971*U[13] + 1.68512035024122*U[14]*U[5] + 1.06132075471698*U[15]*U[7] - 2.66357732780065*U[16]*U[3] - 1.06132075471698*U[16]*U[6] + 2.33490566037736*U[17]*U[5] - 2.33490566037736*U[18]*U[4] + 0.00355660377358491*U[1] + 0.000725706501519649*U[21] - 2.04500508591402e-5*U[29]**3*U[30] - 0.00233715902975374*U[30]\n", + "\tF[13] = 1.6647358298754*U[0]*U[12] - 0.0015418975090707*U[10]**3*U[13] - 1.05320021890077*U[11]*U[1] - 0.0282849932925971*U[12] - 0.0287966213251426*U[13] - 1.68512035024122*U[14]*U[4] + 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] - 1.06132075471698*U[16]*U[7] + 2.33490566037736*U[17]*U[4] + 2.33490566037736*U[18]*U[5] - 0.00355660377358491*U[25] + 4.81842971584593e-5*U[29]**3*U[34] + 0.00355660377358491*U[2] + 0.00550680122860904*U[34]\n", + "\tF[14] = -0.00145929585679905*U[10]**3*U[14] - 1.44050610585137*U[12]*U[5] + 1.44050610585137*U[13]*U[4] - 0.0311378737541528*U[14] - 2.67522562515255*U[15]*U[2] + 2.67522562515255*U[16]*U[1] - 2.88101221170274*U[17]*U[9] + 2.88101221170274*U[18]*U[8] - 5.35045125030509*U[19]*U[7] + 5.35045125030509*U[20]*U[6] + 0.000618363685984819*U[21] - 0.00681189436480877*U[23] - 1.74251833232233e-5*U[29]**3*U[30] + 3.1365329981802e-5*U[29]**3*U[32] - 0.00199145835037292*U[30] + 0.00358462503067126*U[32] + 0.00414285714285714*U[3]\n", + "\tF[15] = -1.35185957626052*U[0]*U[16] - 0.00125723950739611*U[10]**3*U[15] + 0.421071015556554*U[11]*U[5] - 0.45*U[12]*U[7] - 1.63996079743079*U[13]*U[3] + 0.45*U[13]*U[6] + 1.90590038620335*U[14]*U[2] - 0.0368649373881932*U[15] + 0.0230631483770408*U[16] - 2.31923076923077*U[17]*U[2] + 2.31923076923077*U[18]*U[1] + 0.00172785135654124*U[22] - 1.66746568543758e-5*U[29]**3*U[31] - 0.00190568351656843*U[31] + 0.00557692307692308*U[4]\n", + "\tF[16] = 1.35185957626052*U[0]*U[15] - 0.00125723950739611*U[10]**3*U[16] - 0.421071015556554*U[11]*U[4] + 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 0.45*U[13]*U[7] - 1.90590038620335*U[14]*U[1] - 0.0230631483770408*U[15] - 0.0368649373881932*U[16] - 2.31923076923077*U[17]*U[1] - 2.31923076923077*U[18]*U[2] - 0.00557692307692308*U[26] + 3.92887346061283e-5*U[29]**3*U[35] + 0.00449016100178891*U[35] + 0.00557692307692308*U[5]\n", + "\tF[17] = -3.421202001397*U[0]*U[18] - 0.00102150709975934*U[10]**3*U[17] + 0.180063263231421*U[11]*U[7] - 0.7875*U[12]*U[5] - 0.7875*U[13]*U[4] + 0.288101221170274*U[14]*U[9] + 1.4625*U[15]*U[2] + 1.4625*U[16]*U[1] - 0.043546511627907*U[17] + 0.0374776161126912*U[18] - 5.47392320223521*U[20]*U[3] + 9.61561114513536e-5*U[21] - 2.70963173883608e-6*U[29]**3*U[30] - 0.000309673571442371*U[30] + 0.00725*U[6]\n", + "\tF[18] = 3.421202001397*U[0]*U[17] - 0.00102150709975934*U[10]**3*U[18] - 0.180063263231421*U[11]*U[6] + 0.7875*U[12]*U[4] - 0.7875*U[13]*U[5] - 0.288101221170275*U[14]*U[8] - 1.4625*U[15]*U[1] + 1.4625*U[16]*U[2] - 0.0374776161126912*U[17] - 0.043546511627907*U[18] + 5.4739232022352*U[19]*U[3] + 0.00725*U[7]\n", + "\tF[19] = -2.75575081119392*U[0]*U[20] - 0.000888267043268988*U[10]**3*U[19] - 0.250522801017629*U[11]*U[9] + 1.00209120407052*U[14]*U[7] - 4.00836481628207*U[18]*U[3] - 0.0473230535894843*U[19] + 0.0325892314023402*U[20] + 0.00024415290907648*U[22] - 2.35620151203137e-6*U[29]**3*U[31] - 0.000269281366471626*U[31] + 0.00819565217391304*U[8]\n", + "\tF[20] = 2.75575081119392*U[0]*U[19] - 0.000888267043268988*U[10]**3*U[20] + 0.250522801017629*U[11]*U[8] - 1.00209120407052*U[14]*U[6] + 4.00836481628207*U[17]*U[3] - 0.0325892314023402*U[19] - 0.0473230535894843*U[20] + 0.00819565217391304*U[9]\n", + "\tF[21] = 2.30554431681946e-7*U[12] + 2.5547312812995e-7*U[14] + 1.41879650265813e-7*U[17] - 2.30554431681946e-7*U[1] - 4.98594961275655e-7*U[21] - 0.000231547500016414*U[22]*U[25] - 0.000393263214313593*U[22]*U[27] - 0.00077917571434095*U[23]*U[26] - 0.00102174928578672*U[23]*U[28] - 0.00156202678582502*U[24]*U[27] - 2.49430228227486e-5*U[25] - 2.5547312812995e-7*U[3] - 1.41879650265813e-7*U[6]\n", + "\tF[22] = 1.27676499203106e-7*U[0] - 1.27676499203106e-7*U[11] + 4.43165421807242e-7*U[15] + 1.84356815471813e-7*U[19] - 0.000297563940911795*U[21]*U[25] + 0.00056941247952257*U[21]*U[27] - 1.45521268835226e-6*U[22] - 0.00185518259457354*U[23]*U[25] - 0.00363689261114416*U[24]*U[26] - 2.49312940023328e-5*U[26] - 4.43165421807242e-7*U[4] - 1.84356815471813e-7*U[8]\n", + "\tF[23] = -4.59275459509595e-7*U[14] - 0.00110122563125871*U[21]*U[26] + 0.0019601816236405*U[21]*U[28] + 0.000385428970940548*U[22]*U[25] - 3.04757790976247e-6*U[23] - 0.00547309138735578*U[24]*U[25] - 2.49117704610167e-5*U[27] + 4.59275459509595e-7*U[3]\n", + "\tF[24] = 5.09747214298137e-8*U[0] - 5.09747214298137e-8*U[11] - 0.0028417174170155*U[21]*U[27] - 0.000594010608460015*U[22]*U[26] + 0.00259237963074834*U[23]*U[25] - 5.2727039845599e-6*U[24] - 2.48844888171634e-5*U[28]\n", + "\tF[25] = -5.43087366736076e-7*U[13] + 0.000529111412123567*U[21]*U[22] + 2.49364240037345e-5*U[21] + 0.00146975392256546*U[22]*U[23] + 0.00288071768822831*U[23]*U[24] - 1.03680315467796e-6*U[25] + 5.43087366736076e-7*U[2]\n", + "\tF[26] = -1.04390781015316e-6*U[16] + 0.00188040062849988*U[21]*U[23] + 0.00423090141412473*U[22]*U[24] + 2.49247013868922e-5*U[22] - 1.99291491029239e-6*U[26] + 1.04390781015316e-6*U[5]\n", + "\tF[27] = -0.000176149545706673*U[21]*U[22] + 0.00440373864266684*U[21]*U[24] + 2.49051881654425e-5*U[23] - 3.58443843007766e-6*U[27]\n", + "\tF[28] = -0.000938435679208948*U[21]*U[23] + 2.4877920928742e-5*U[24] - 5.80838943890229e-6*U[28]\n", + "\tF[29] = 9.12059910499408e-6*U[10]**4 + 4.64878611115251e-5*U[10]**3*U[12] + 1.67415119447806e-5*U[10]**3*U[14] + 9.29757222230503e-6*U[10]**3*U[17] + 0.000521179401993356*U[10] + 0.000664115245477357*U[12] + 0.000239165516524779*U[14] + 0.000132823049095472*U[17] + 1.50119582264922*U[21]*U[35] - 0.750597911324609*U[21]*U[37] - 1.50119582264922*U[22]*U[34] + 1.12589686698692*U[22]*U[36] - 4.50358746794765*U[23]*U[35] + 2.25179373397383*U[23]*U[37] - 2.62709268963613*U[24]*U[34] - 5.62948433493457*U[24]*U[36] + 1.50119582264922*U[25]*U[31] + 2.62709268963613*U[25]*U[33] - 1.50119582264922*U[26]*U[30] + 4.50358746794765*U[26]*U[32] - 1.12589686698692*U[27]*U[31] + 5.62948433493457*U[27]*U[33] + 0.750597911324609*U[28]*U[30] - 2.25179373397383*U[28]*U[32] - 8.14339205803043e-7*U[29]**4 - 0.000260589700996678*U[29] + 5.70773716822425e-5\n", + "\tF[30] = -3.54406469365901e-21*U[10]**4 - 5.31652508387687e-5*U[10]**3*U[12] - 2.75103051082387e-5*U[10]**3*U[14] - 1.06330501677538e-5*U[10]**3*U[17] - 2.31450148821357e-19*U[10] - 0.000759506950146622*U[12] - 0.000393006100803065*U[14] - 0.000151901390029325*U[17] - 1.21682350103801*U[21]*U[35] + 0.608411750519007*U[21]*U[37] + 2.34182350103802*U[22]*U[34] - 1.28761762577851*U[22]*U[36] + 5.15047050311404*U[23]*U[35] - 2.57523525155702*U[23]*U[37] + 2.12944112681652*U[24]*U[34] + 6.43808812889255*U[24]*U[36] - 2.34182350103802*U[25]*U[31] - 2.12944112681652*U[25]*U[33] + 1.21682350103801*U[26]*U[30] - 5.15047050311404*U[26]*U[32] + 1.28761762577851*U[27]*U[31] - 6.43808812889255*U[27]*U[33] - 0.608411750519007*U[28]*U[30] + 2.57523525155702*U[28]*U[32] + 3.16434347648126e-22*U[29]**4 - 3.25735682321217e-6*U[29]**3*U[30] - 0.000260589700996678*U[30] - 2.21790142793497e-20\n", + "\tF[31] = 2.78802933171573e-5*U[10]**3*U[11] - 1.54836099362061e-5*U[10]**3*U[15] - 3.09672198724123e-6*U[10]**3*U[19] + 0.000398291670074584*U[11] - 0.000221195408173122*U[15] - 4.42390816346244e-5*U[19] - 1.125*U[21]*U[34] + 0.375*U[21]*U[36] + 1.875*U[23]*U[34] + 2.25*U[24]*U[35] + 1.125*U[25]*U[30] - 1.875*U[25]*U[32] - 2.25*U[26]*U[33] - 0.375*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[31] - 0.000260589700996678*U[31] + 4.36192251724445e-5\n", + "\tF[32] = -5.06294956237001e-22*U[10]**4 - 1.25605469675209e-5*U[10]**3*U[12] + 2.05688778355549e-5*U[10]**3*U[14] - 2.51210939350418e-6*U[10]**3*U[17] - 0.000179437180657833*U[12] + 0.000293842414478535*U[14] - 3.58874361315667e-5*U[17] - 1.90560783367934*U[21]*U[35] + 0.952803916839669*U[21]*U[37] - 1.46939216632066*U[22]*U[34] - 0.304205875259504*U[22]*U[36] + 1.21682350103801*U[23]*U[35] - 0.608411750519007*U[23]*U[37] + 3.33481370893884*U[24]*U[34] + 1.52102937629752*U[24]*U[36] + 1.46939216632066*U[25]*U[31] - 3.33481370893884*U[25]*U[33] + 1.90560783367934*U[26]*U[30] - 1.21682350103801*U[26]*U[32] + 0.304205875259504*U[27]*U[31] - 1.52102937629752*U[27]*U[33] - 0.952803916839669*U[28]*U[30] + 0.608411750519007*U[28]*U[32] + 4.52049068068751e-23*U[29]**4 + 3.61639254455001e-22*U[29]**3*U[30] - 3.25735682321217e-6*U[29]**3*U[32] - 0.000260589700996678*U[32] - 3.16843061133567e-21\n", + "\tF[33] = 1.11521173268629e-5*U[10]**3*U[11] + 0.000159316668029834*U[11] - 1.875*U[21]*U[36] - 2.25*U[22]*U[35] - 2.625*U[23]*U[34] + 2.625*U[25]*U[32] + 2.25*U[26]*U[31] + 1.875*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[33] - 0.000260589700996678*U[33] + 1.74476900689778e-5\n", + "\tF[34] = 3.64823964199763e-5*U[10]**3*U[13] + 0.000521179401993355*U[13] + 1.125*U[21]*U[31] - 1.125*U[22]*U[30] + 1.875*U[22]*U[32] - 1.875*U[23]*U[31] + 2.625*U[23]*U[33] - 2.625*U[24]*U[32] - 3.25735682321217e-6*U[29]**3*U[34] - 0.000260589700996678*U[34]\n", + "\tF[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", + "\tF[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", + "\tF[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", + "\treturn F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e34a926e", + "metadata": {}, + "outputs": [], + "source": [ + "ten.tensor_dic" + ] + }, { "cell_type": "code", "execution_count": null, @@ -355,6 +555,17 @@ "integrator_num.set_func(f_num)" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "6fa65680", + "metadata": {}, + "outputs": [], + "source": [ + "integrator_num = RungeKuttaIntegrator()\n", + "integrator_num.set_func(f_test)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -382,7 +593,7 @@ "outputs": [], "source": [ "%%time\n", - "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", + "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=10)\n", "reference_time, reference_traj = integrator.get_trajectories()" ] }, @@ -394,10 +605,22 @@ "outputs": [], "source": [ "%%time\n", - "integrator_num.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", + "integrator_num.integrate(0., 1000000., 0.1, ic=ic, write_steps=10)\n", "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4a93eca", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator_num.integrate(0., 1000000., 0.1, ic=ic, write_steps=10)\n", + "reference_time_test, reference_traj_test = integrator_num.get_trajectories()" + ] + }, { "cell_type": "code", "execution_count": null, @@ -411,6 +634,7 @@ "\n", "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", + "plt.plot(reference_traj_test[varx], reference_traj_test[vary], marker='o', ms=0.07, ls='')\n", "\n", "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" @@ -425,8 +649,9 @@ "source": [ "plt.figure(figsize=(12, 8))\n", "\n", - "plt.plot(reference_traj_num[varx, :])\n", "plt.plot(reference_traj[varx, :])\n", + "plt.plot(reference_traj_num[varx, :])\n", + "plt.plot(reference_traj_test[varx, :])\n", "plt.show()" ] }, @@ -441,6 +666,7 @@ "\n", "plt.plot(reference_traj_num[vary, :])\n", "plt.plot(reference_traj[vary, :])\n", + "plt.plot(reference_traj_test[vary, :])\n", "plt.show()" ] }, @@ -450,6 +676,95 @@ "id": "6ed354fa", "metadata": {}, "outputs": [], + "source": [ + "plt.figure(figsize=(12, 8))\n", + "\n", + "plt.plot(reference_traj_num[varx, ::1000])\n", + "plt.plot(reference_traj[varx, ::1000])\n", + "plt.plot(reference_traj_test[varx, ::1000])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "457b65e1", + "metadata": {}, + "outputs": [], + "source": [ + "tendencies_sym = np.empty((38, reference_time[::1000].shape[0]))\n", + "for n, x in enumerate(reference_time[::1000]):\n", + " x = reference_traj_num[:, n]\n", + " tendencies_sym[:, n] = f(0, x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7ced08cb", + "metadata": {}, + "outputs": [], + "source": [ + "tendencies_sym.shape" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bd90544e", + "metadata": {}, + "outputs": [], + "source": [ + "tendencies_num = np.empty_like(tendencies_sym)\n", + "for n, x in enumerate(reference_time[::1000]):\n", + " x = reference_traj_num[:, n]\n", + " tendencies_num[:, n] = f_num(0, x)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8ed76ce", + "metadata": {}, + "outputs": [], + "source": [ + "tendencies_err = tendencies_sym - tendencies_num" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c2303e5", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 8))\n", + "varx = 10\n", + "plt.plot(tendencies_err[:, :].T)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0cdf273c", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 8))\n", + "varx = [1, 12]\n", + "plt.plot(tendencies_err[varx, :].T)\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4adeeca5", + "metadata": {}, + "outputs": [], "source": [] } ], diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 82db852..443bc33 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -27,8 +27,6 @@ '**': '^' } -# TODO: this function isnt working with user input IPs, still calculated them from scratch - def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables=list(), language='python', return_inner_products=False, return_jacobian=False, return_symbolic_eqs=False, return_symbolic_qgtensor=False): """ Function to output the raw symbolic functions of the qgs model. @@ -43,7 +41,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con Allows for stored inner products to be input gnd_ip: SymbolicGroundInnerProducts, optional Allows for stored inner products to be input - continuation_variables: Set or List or None + continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) The variables to not substitute and to leave in the equations, if None no variables are substituted language: String Options for the output language syntax: 'python', 'julia', 'fortran', 'auto', 'mathematica' @@ -70,6 +68,8 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con If `return_symbolic_qgtensor` is True, the symbolic tendencies tensor of the system. """ + make_ip_subs = True + if continuation_variables is None: make_ip_subs = False else: @@ -79,8 +79,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con make_ip_subs = False except: pass - else: - make_ip_subs = True + if not(make_ip_subs): warnings.warn("Calculating innerproducts symbolically, as the variable 'n' has been specified as a variable, this takes several minutes.") @@ -420,7 +419,7 @@ def create_auto_file(equations, params, continuation_variables): Dictionary of the substituted symbolic model equations params: QGParams The parameters fully specifying the model configuration. - continuation_variables: Set or List or None + continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) Variables that are not substituted with numerical values. If None, no symbols are substituted ''' diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 9bb5c73..80e42cb 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -814,8 +814,7 @@ def sub_tensor(self, tensor=None, continuation_variables=list()): ---------- tensor: dict, sympy array - continuation_variables: Parameter, ScalingParameter - iterable of variables not to substitute + continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) if None all variables are substituted. This variable is the opposite of 'variables' Returns @@ -984,7 +983,7 @@ def _compute_stored_full_dict(self): a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[(i, j)] = par.atmospheric_parameters.sig0.symbol * aips.a(i, j) - aips.u(i, j) + a_theta[(i, j)] = par.atmospheric_params.sig0.symbol * aips.a(i, j) - aips.u(i, j) a_theta = ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() @@ -1113,7 +1112,7 @@ def _compute_non_stored_full_dict(self): a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[(i, j)] = ap.sig0.symbol * aips.a(i, j) - aips.u(i, j) + a_theta[(i, j)] = par.atmospheric_params.sig0.symbol * aips.a(i, j) - aips.u(i, j) a_theta = ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() @@ -1311,7 +1310,7 @@ def _compute_non_stored_full_dict(self): a_theta = dict() for i in range(nvar[1]): for j in range(nvar[1]): - a_theta[(i, j)] = ap.sig0.symbol * aips.a(i, j) - aips.u(i, j) + a_theta[(i, j)] = par.atmospheric_params.sig0.symbol * aips.a(i, j) - aips.u(i, j) a_theta = ImmutableSparseMatrix(nvar[1], nvar[1], a_theta) a_theta = a_theta.inverse() From 33c32cfa11bd00da30e7aa26b53865c25d149740 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 15 Sep 2023 17:36:38 +0200 Subject: [PATCH 062/143] Update to auto outputs --- notebooks/Symbolic Output-Linear.ipynb | 16 +++++-- qgs/functions/.cproto | 4 +- qgs/functions/.modelproto | 2 +- qgs/functions/symbolic_output.py | 62 +++++++++++++------------- 4 files changed, 48 insertions(+), 36 deletions(-) diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 9b7d56c..3197316 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -119,12 +119,22 @@ "from sympy.tensor.array import ImmutableDenseNDimArray" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "21592dd9", + "metadata": {}, + "outputs": [], + "source": [ + "str(model_parameters.atemperature_params.eps.symbol)" + ] + }, { "cell_type": "code", "execution_count": null, "id": "0165d8ea", "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ @@ -146,7 +156,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, _parameter_substitutions" + "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, _parameter_values" ] }, { @@ -177,7 +187,7 @@ "outputs": [], "source": [ "%%time\n", - " funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.atemperature_params.eps], language='python', return_symbolic_qgtensor=True)" + "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.rr], language='auto', return_symbolic_qgtensor=True)" ] }, { diff --git a/qgs/functions/.cproto b/qgs/functions/.cproto index f5e766a..89561b7 100644 --- a/qgs/functions/.cproto +++ b/qgs/functions/.cproto @@ -29,6 +29,6 @@ EPSL=1e-07, EPSU=1e-07, EPSS=1e-05 #Number of parameter (don't change it) NPAR = 36 #User defined value where to save the solution -UZR={'coup': [0.007, 0.0075, 0.008, 0.0085, 0.009, 0.0095, 0.01, 0.0105, 0.011, 0.0115, 0.012, 0.0125, 0.013, 0.0135, 0.014, 0.0145, 0.015]} +# ! SOLUTION SAVE #Stop conditions -UZSTOP = {'coup': [0.005, 0.02],'epsa': [0.5,1]} +# ! STOP CONDITIONS diff --git a/qgs/functions/.modelproto b/qgs/functions/.modelproto index b8bd1dc..61cabc2 100644 --- a/qgs/functions/.modelproto +++ b/qgs/functions/.modelproto @@ -1,6 +1,6 @@ !---------------------------------------------------------------------- !---------------------------------------------------------------------- -! model_aot_VDDG.f90 - OAQGWFTS ocean-atmosphere coupled model (with temperatures) +! AUTO file for qgs model !---------------------------------------------------------------------- !---------------------------------------------------------------------- diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 443bc33..fad204c 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -4,7 +4,7 @@ import warnings from qgs.functions.symbolic_mul import symbolic_sparse_mult2, symbolic_sparse_mult3, symbolic_sparse_mult4, symbolic_sparse_mult5 from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, GroundSymbolicInnerProducts -from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT, SymbolicTensorT4, _parameter_substitutions +from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT, SymbolicTensorT4 import os @@ -271,15 +271,10 @@ def format_equations(equations, params, save_loc=None, language='python', print_ for i in range(1, params.ndim+1): vector_subs['U_'+str(i)] = sy.Symbol('U('+str(i)+')') - free_vars = set() for k in equations.keys(): eq = equations[k].subs(vector_subs) eq = eq.evalf() - for vars in eq.free_symbols: - if vars not in vector_subs.values(): - free_vars.add(vars) - if (language is not None) and print_equations: eq = translate_equations(eq, language) @@ -296,9 +291,9 @@ def format_equations(equations, params, save_loc=None, language='python', print_ f.write("%s\n" % eq) print("Equations written") else: - return equation_dict, free_vars + return equation_dict -def equation_as_function(equations, params, string_output=True, language='python', continuation_variables=dict()): +def equation_as_function(equations, params, string_output=True, language='python', continuation_variables=list()): ''' Converts the symbolic equations to a function in string format in the language syntax specified, or a lambdified python function @@ -321,7 +316,7 @@ def equation_as_function(equations, params, string_output=True, language='python ''' - eq_dict, free_vars = format_equations(equations, params, language=language) + eq_dict = format_equations(equations, params, language=language) f_output = list() if language == 'python': @@ -330,8 +325,9 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('def f(t, U, **kwargs):') f_output.append('\t#Tendency function of the qgs model') f_output.append('\tF = np.empty_like(U)') - for v in free_vars: - f_output.append('\t' + str(v) + " = kwargs['" + str(v) + "']") + + for v in continuation_variables: + f_output.append('\t' + str(v) + " = kwargs['" + str(v.symbol) + "']") for n, eq in enumerate(eq_dict.values()): f_output.append('\tF['+str(n)+'] = ' + str(eq)) @@ -343,8 +339,8 @@ def equation_as_function(equations, params, string_output=True, language='python array_eqs = np.array(list(eq_dict.values())) inputs = ['t', vec] - for v in free_vars: - inputs.append(v) + for v in continuation_variables: + inputs.append(v.symbol) f_output = sy.lambdify(inputs, array_eqs) @@ -354,8 +350,9 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('function f(t, U, kwargs...)') f_output.append('\t#Tendency function of the qgs model') f_output.append('\tF = similar(U)') - for v in free_vars: - f_output.append('\t' + str(v) + " = kwargs['" + str(v) + "']") + + for v in continuation_variables: + f_output.append('\t' + str(v) + " = kwargs['" + str(v.symbol) + "']") for n, eq in enumerate(eq_dict.values()): f_output.append('\tF['+str(n+1)+'] = ' + str(eq)) @@ -367,9 +364,9 @@ def equation_as_function(equations, params, string_output=True, language='python eq_dict = translate_equations(eq_dict, language='fortran') f_var = '' - if len(free_vars) > 0: - for fv in free_vars: - f_var += str(fv) + ', ' + if len(continuation_variables) > 0: + for fv in continuation_variables: + f_var += str(fv.symbol) + ', ' f_output.append('SUBROUTINE FUNC(NDIM, t, U, F, ' + f_var[:-2] + ')') else: f_output.append('SUBROUTINE FUNC(NDIM, t, U, F)') @@ -379,8 +376,8 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*)') f_output.append('\tDOUBLE PRECISION, INTENT(OUT) :: F(NDIM)') - for v in free_vars: - f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: ' + str(v)) + for v in continuation_variables: + f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: ' + str(v.symbol)) f_output.append('') @@ -392,7 +389,7 @@ def equation_as_function(equations, params, string_output=True, language='python eq_dict = translate_equations(eq_dict, language='fortran') eq_dict = _split_equations(eq_dict, f_output) - create_auto_file(eq_dict, params, free_vars) + create_auto_file(eq_dict, params, continuation_variables) if language == 'mathematica': #TODO: This function needs testing before release @@ -440,19 +437,18 @@ def create_auto_file(equations, params, continuation_variables): # Declare variables declare_var = list() - for i in continuation_variables: - declare_var.append('DOUBLE PRECISION ' + str(i)) + for v in continuation_variables: + declare_var.append('DOUBLE PRECISION ' + str(v.symbol)) # make list of parameters var_list = list() var_ini = list() - model_parameters = _parameter_substitutions(params, {}) + for i, v in enumerate(continuation_variables): - for i, cv in enumerate(continuation_variables): - temp_str = "PAR(" + str(i) + ") = " + str(cv.symbol) + temp_str = "PAR(" + str(i) + ") = " + str(v.symbol) - initial_value = "PAR(" + str(i) + ") = " + str(cv) + " Variable: " + str(cv.symbol) + initial_value = "PAR(" + str(i) + ") = " + str(v) + " Variable: " + str(v.symbol) var_list.append(temp_str) var_ini.append(initial_value) @@ -493,7 +489,7 @@ def create_auto_file(equations, params, continuation_variables): auto_config = list() for ln in lines: if '! PARAMETERS' in ln: - auto_config.append('parnames = ' + str({i+1: v for i, v in enumerate(continuation_variables)})) + auto_config.append('parnames = ' + str({i+1: v.symbol for i, v in enumerate(continuation_variables)})) elif '! VARIABLES' in ln: auto_config.append('unames = ' + str(_variable_names(params))) @@ -502,9 +498,15 @@ def create_auto_file(equations, params, continuation_variables): auto_config.append('NDIM = ' + str(params.ndim)) elif '! CONTINUATION ORDER' in ln: - auto_config.append('ICP = ' + str(list(continuation_variables))) + auto_config.append('ICP = ' + str([v.symbol for v in continuation_variables])) - #TODO: Need to include bounds on continuation parameters + elif '! SOLUTION SAVE' in ln: + auto_config.append("# ! User to input save locations") + auto_config.append('UZR = ' + str({v.symbol: [] for v in continuation_variables})) + + elif '! STOP CONDITIONS' in ln: + auto_config.append("# ! User to input variable bounds") + auto_config.append('UZSTOP = ' + str({v.symbol: [] for v in continuation_variables})) else: auto_config.append(ln.replace('\n', '')) From 752e985cf61a7e3d7bd56a2e0220df8e2159705d Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 15 Sep 2023 17:39:08 +0200 Subject: [PATCH 063/143] Cleaning library imports --- qgs/params/params.py | 2 +- qgs/tensors/symbolic_qgtensor.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/qgs/params/params.py b/qgs/params/params.py index 1d4e1f4..6cf772b 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -50,7 +50,7 @@ from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis -from sympy import Symbol, simplify, ImmutableSparseMatrix +from sympy import Symbol, simplify # TODO: - store model version in a variable somewhere # - force or warn the user to define the aspect ratio n at parameter object instantiation diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 80e42cb..eedd7c7 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -9,7 +9,6 @@ from qgs.params.params import Parameter, ScalingParameter, ParametersArray import numpy as np -import sparse as sp import sympy as sy import pickle From 526e28b519b94b341f505c7fb9aea117d854d751 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 29 Sep 2023 13:21:42 +0200 Subject: [PATCH 064/143] Edit to julia output to make it work in DifferentialEquations library --- qgs/functions/symbolic_output.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index fad204c..15e0511 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -20,7 +20,8 @@ julia_lang_translation = { '**': '^', - 'pi': 'pi()' + 'pi': 'pi()', + 'conjugate': 'conj' } mathematica_lang_translation = { @@ -347,7 +348,7 @@ def equation_as_function(equations, params, string_output=True, language='python if language == 'julia': eq_dict = translate_equations(eq_dict, language='julia') - f_output.append('function f(t, U, kwargs...)') + f_output.append('function f!(du, U, p, t)') f_output.append('\t#Tendency function of the qgs model') f_output.append('\tF = similar(U)') @@ -355,7 +356,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('\t' + str(v) + " = kwargs['" + str(v.symbol) + "']") for n, eq in enumerate(eq_dict.values()): - f_output.append('\tF['+str(n+1)+'] = ' + str(eq)) + f_output.append('\tdu['+str(n+1)+'] = ' + str(eq)) f_output.append('\treturn F') f_output.append('end') From 596c2baffc04271f0b4973bb21e3def6363e0d18 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 29 Sep 2023 17:05:34 +0200 Subject: [PATCH 065/143] Tidy up of julia output --- qgs/functions/symbolic_output.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 15e0511..2097f70 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -350,7 +350,6 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('function f!(du, U, p, t)') f_output.append('\t#Tendency function of the qgs model') - f_output.append('\tF = similar(U)') for v in continuation_variables: f_output.append('\t' + str(v) + " = kwargs['" + str(v.symbol) + "']") @@ -358,7 +357,6 @@ def equation_as_function(equations, params, string_output=True, language='python for n, eq in enumerate(eq_dict.values()): f_output.append('\tdu['+str(n+1)+'] = ' + str(eq)) - f_output.append('\treturn F') f_output.append('end') if language == 'fortran': From 182722cea9df3e07c878f98893c0d49a6b41ca08 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 29 Sep 2023 17:35:44 +0200 Subject: [PATCH 066/143] Notebook cleaning --- notebooks/Symbolic Output-Linear.ipynb | 2 +- notebooks/ground_heat.ipynb | 127 ++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 13 deletions(-) diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 3197316..6c85eb6 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -187,7 +187,7 @@ "outputs": [], "source": [ "%%time\n", - "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.rr], language='auto', return_symbolic_qgtensor=True)" + "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.scale_params.n], language='auto', return_symbolic_qgtensor=True)" ] }, { diff --git a/notebooks/ground_heat.ipynb b/notebooks/ground_heat.ipynb index 168b078..f5b74db 100644 --- a/notebooks/ground_heat.ipynb +++ b/notebooks/ground_heat.ipynb @@ -44,7 +44,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -53,7 +53,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -62,7 +62,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -80,7 +80,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -96,7 +96,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -122,7 +122,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -144,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -164,7 +164,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -174,9 +174,85 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Qgs v0.2.8 parameters summary\n", + "=============================\n", + "\n", + "General Parameters:\n", + "'dynamic_T': False,\n", + "'T4': False,\n", + "'time_unit': days,\n", + "'rr': 287.058 [J][kg^-1][K^-1] (gas constant of dry air),\n", + "'sb': 5.67e-08 [J][m^-2][s^-1][K^-4] (Stefan-Boltzmann constant),\n", + "\n", + "Scale Parameters:\n", + "'scale': 5000000.0 [m] (characteristic space scale (L*pi)),\n", + "'f0': 0.0001032 [s^-1] (Coriolis parameter at the middle of the domain),\n", + "'n': 1.5 [nondim] (aspect ratio (n = 2 L_y / L_x)),\n", + "'rra': 6370000.0 [m] (earth radius),\n", + "'phi0_npi': 0.2777777777777778 [nondim] (latitude expressed in fraction of pi),\n", + "'deltap': 50000.0 [Pa] (pressure difference between the two atmospheric layers),\n", + "'Ha': 8500.0 [m] (Average height of the 500 hPa pressure level at midlatitude),\n", + "\n", + "Atmospheric Parameters:\n", + "'kd': 0.1 [nondim] (atmosphere bottom friction coefficient),\n", + "'kdp': 0.01 [nondim] (atmosphere internal friction coefficient),\n", + "'sigma': 0.2 [nondim] (static stability of the atmosphere),\n", + "\n", + "Atmospheric Temperature Parameters:\n", + "'gamma': 10000000.0 [J][m^-2][K^-1] (specific heat capacity of the atmosphere),\n", + "'C[1]': 99.0 [W][m^-2] (spectral component 1 of the short-wave radiation of the atmosphere),\n", + "'C[2]': 0.0 [W][m^-2] (spectral component 2 of the short-wave radiation of the atmosphere),\n", + "'C[3]': 0.0 [W][m^-2] (spectral component 3 of the short-wave radiation of the atmosphere),\n", + "'C[4]': 0.0 [W][m^-2] (spectral component 4 of the short-wave radiation of the atmosphere),\n", + "'C[5]': 0.0 [W][m^-2] (spectral component 5 of the short-wave radiation of the atmosphere),\n", + "'C[6]': 0.0 [W][m^-2] (spectral component 6 of the short-wave radiation of the atmosphere),\n", + "'C[7]': 0.0 [W][m^-2] (spectral component 7 of the short-wave radiation of the atmosphere),\n", + "'C[8]': 0.0 [W][m^-2] (spectral component 8 of the short-wave radiation of the atmosphere),\n", + "'C[9]': 0.0 [W][m^-2] (spectral component 9 of the short-wave radiation of the atmosphere),\n", + "'C[10]': 0.0 [W][m^-2] (spectral component 10 of the short-wave radiation of the atmosphere),\n", + "'eps': 0.76 (emissivity coefficient for the grey-body atmosphere),\n", + "'T0': 270.0 [K] (stationary solution for the 0-th order atmospheric temperature),\n", + "'sc': 1.0 (ratio of surface to atmosphere temperature),\n", + "'hlambda': 10.0 [W][m^-2][K^-1] (sensible+turbulent heat exchange between ocean/ground and atmosphere),\n", + "\n", + "Ground Parameters:\n", + "'hk[1]': 0.0 (spectral component 1 of the orography),\n", + "'hk[2]': 0.2 (spectral components 2 of the orography),\n", + "'hk[3]': 0.0 (spectral component 3 of the orography),\n", + "'hk[4]': 0.0 (spectral component 4 of the orography),\n", + "'hk[5]': 0.0 (spectral component 5 of the orography),\n", + "'hk[6]': 0.0 (spectral component 6 of the orography),\n", + "'hk[7]': 0.0 (spectral component 7 of the orography),\n", + "'hk[8]': 0.0 (spectral component 8 of the orography),\n", + "'hk[9]': 0.0 (spectral component 9 of the orography),\n", + "'hk[10]': 0.0 (spectral component 10 of the orography),\n", + "'orographic_basis': atmospheric,\n", + "\n", + "Ground Temperature Parameters:\n", + "'gamma': 16000000.0 [J][m^-2][K^-1] (specific heat capacity of the ground),\n", + "'C[1]': 300.0 [W][m^-2] (spectral component 1 of the short-wave radiation of the ground),\n", + "'C[2]': 0.0 [W][m^-2] (spectral component 2 of the short-wave radiation of the ground),\n", + "'C[3]': 0.0 [W][m^-2] (spectral component 3 of the short-wave radiation of the ground),\n", + "'C[4]': 0.0 [W][m^-2] (spectral component 4 of the short-wave radiation of the ground),\n", + "'C[5]': 0.0 [W][m^-2] (spectral component 5 of the short-wave radiation of the ground),\n", + "'C[6]': 0.0 [W][m^-2] (spectral component 6 of the short-wave radiation of the ground),\n", + "'C[7]': 0.0 [W][m^-2] (spectral component 7 of the short-wave radiation of the ground),\n", + "'C[8]': 0.0 [W][m^-2] (spectral component 8 of the short-wave radiation of the ground),\n", + "'C[9]': 0.0 [W][m^-2] (spectral component 9 of the short-wave radiation of the ground),\n", + "'C[10]': 0.0 [W][m^-2] (spectral component 10 of the short-wave radiation of the ground),\n", + "'T0': 285.0 [K] (stationary solution for the 0-th order oceanic temperature),\n", + "\n", + "\n" + ] + } + ], "source": [ "# Printing the model's parameters\n", "model_parameters.print_params()" @@ -191,9 +267,36 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": {}, "outputs": [], + "source": [ + "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "aip = AtmosphericSymbolicInnerProducts(model_parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 8.88 s, sys: 1.12 s, total: 10 s\n", + "Wall time: 8.55 s\n" + ] + } + ], "source": [ "%%time\n", "f, Df = create_tendencies(model_parameters)" From 9d559d2915944a792642cafc34ede4b356b13c75 Mon Sep 17 00:00:00 2001 From: ushham Date: Sat, 30 Sep 2023 15:59:59 +0200 Subject: [PATCH 067/143] bug and catches fixed in non-stored version --- qgs/tensors/symbolic_qgtensor.py | 72 ++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index eedd7c7..8b4730b 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -149,7 +149,10 @@ def _deltaT_g(self, i): @property def LR(self): - return sy.sqrt(self.params.oceanic_params.gp.symbol * self.params.oceanic_params.h.symbol) / self.params.scale_params.f0.symbol + if self.params.oceanic_params.gp is None or self.params.oceanic_params.h is None: + return None + else: + return sy.sqrt(self.params.oceanic_params.gp.symbol * self.params.oceanic_params.h.symbol) / self.params.scale_params.f0.symbol @property def G(self): @@ -157,21 +160,33 @@ def G(self): @property def Cpgo(self): - C = ImmutableSparseNDimArray(self.params.gotemperature_params.C.symbols) - return C / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) * self.params.rr.symbol / (self.params.scale_params.f0.symbol ** 2 * self.params.scale_params.L.symbol ** 2) + if self.params.gotemperature_params.C is None: + return None + else: + C = ImmutableSparseNDimArray(self.params.gotemperature_params.C.symbols) + return C / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) * self.params.rr.symbol / (self.params.scale_params.f0.symbol ** 2 * self.params.scale_params.L.symbol ** 2) @property def Lpgo(self): - return self.params.atemperature_params.hlambda.symbol / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) + if self.params.atemperature_params.hlambda is None or self.params.gotemperature_params.gamma is None: + return None + else: + return self.params.atemperature_params.hlambda.symbol / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) @property def Cpa(self): - C = ImmutableSparseNDimArray(self.params.atemperature_params.C.symbols) - return C / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) * self.params.rr.symbol / (self.params.scale_params.f0.symbol ** 2 * self.params.scale_params.L.symbol ** 2) / 2 + if self.params.atemperature_params.C is None: + return None + else: + C = ImmutableSparseNDimArray(self.params.atemperature_params.C.symbols) + return C / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) * self.params.rr.symbol / (self.params.scale_params.f0.symbol ** 2 * self.params.scale_params.L.symbol ** 2) / 2 @property def Lpa(self): - return self.params.atemperature_params.hlambda.symbol / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) + if self.params.atemperature_params.hlambda is None or self.params.atemperature_params.gamma is None: + return None + else: + return self.params.atemperature_params.hlambda.symbol / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) @property def sbpgo(self): @@ -357,7 +372,7 @@ def _compute_tensor_dicts(self): val_Cpa = _symbolic_tensordot(a_theta_mult_u , self.Cpa, axes=1) if atp.hd is not None and atp.thetas is not None: - thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbol) + thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbols) val_thetas = _symbolic_tensordot(a_theta_mult_u, thetas_sym_arr, axes=1) # not perfect a_theta_mult_a = _symbolic_tensordot(a_theta, aips._a[:, offset:], axes=1) @@ -548,16 +563,20 @@ def _compute_tensor_dicts(self): # theta_a part for i in range(nvar[1]): if self.Cpa is not None: + val = 0 for jj in range(nvar[1]): for kk in range(nvar[1]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), - a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk]) + val -= a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk] + + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val) if atp.hd is not None and atp.thetas is not None: - thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbol) + thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbols) val = 0 for jj in range(nvar[1]): for kk in range(nvar[1]): val -= a_theta[i, jj] * aips.u(jj, kk) * thetas_sym_arr[kk] + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val * atp.hd.symbol) for j in range(nvar[0]): @@ -567,12 +586,14 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.a(jj, jo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val * ap.kd.symbol * ap.sig0.symbol / 2) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - val * (ap.kd.symbol / 2 - 2 * ap.kdp.symbol) * ap.sig0.symbol) val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.c(jj, jo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), val * par.scale_params.beta.symbol * ap.sig0.symbol) if gp is not None: @@ -594,6 +615,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.b(jj, jo, ko) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - val * ap.sig0.symbol) sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - val * ap.sig0.symbol) @@ -610,6 +632,7 @@ def _compute_tensor_dicts(self): if self.Lpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.sc.symbol * self.Lpa) + if self.LSBpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.LSBpa) @@ -622,7 +645,8 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.d(jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * ap.sig0.symbol * ap.kd.symbol / 2) + + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * ap.sig0.symbol * ap.kd.symbol / 2) if self.Lpa is not None: for j in range(nvar[3]): @@ -651,8 +675,8 @@ def _compute_tensor_dicts(self): for jj in range(nvar[2]): val = M_psio[i, jj] * bips.K(offset + jj, jo) * par.oceanic_params.d.symbol - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), val) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), - val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), val) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), - val) for j in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists @@ -669,14 +693,19 @@ def _compute_tensor_dicts(self): for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists + val = 0 for jj in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio[i, jj] * bips.C(offset + jj, jo, ko)) + val -= M_psio[i, jj] * bips.C(offset + jj, jo, ko) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), val) # deltaT_o part for i in range(nvar[3]): + val = 0 for jj in range(nvar[1]): for kk in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj]) + val += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] + + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), val) for j in range(nvar[1]): val = 0 @@ -694,21 +723,28 @@ def _compute_tensor_dicts(self): for j in range(nvar[2]): for k in range(offset, nvar[3]): jo = j + offset # skipping the T 0 variable if it exists + + val = 0 for jj in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(k)), - U_inv[i, jj] * bips.O(jj, jo, k)) + val -= U_inv[i, jj] * bips.O(jj, jo, k) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(k)), val) # deltaT_g part if ground_temp: for i in range(nvar[2]): + val = 0 for jj in range(nvar[1]): for kk in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj]) + val += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] + + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), val) for j in range(nvar[1]): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips.W(jj, j) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) + sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) + if self.sbpa is not None: sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.sbpa) From 8a25fae405250bfa61aecb1c2f737e37778c5c5e Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 6 Oct 2023 10:43:11 +0200 Subject: [PATCH 068/143] Reverting change to make the params setter unified for ground and ocean temperature (for now, let's see) --- qgs/params/params.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/qgs/params/params.py b/qgs/params/params.py index 6cf772b..d00111e 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -664,7 +664,6 @@ def _create_orography(self, values): dim = values values = dim * [0.] - d = ["spectral component "+str(pos+1)+" of the orography" for pos in range(dim)] self.hk = ParametersArray(values, scale_object=self._scale_params, @@ -1177,19 +1176,13 @@ def set_params(self, dic): if self.ground_params is not None: self.ground_params.set_params(dic) - #TODO: Needs checking! - - if 'gotemperature_params' in self.__dict__.keys(): + if 'otemperature_params' in self.__dict__.keys(): if self.gotemperature_params is not None: self.gotemperature_params.set_params(dic) - # if 'otemperature_params' in self.__dict__.keys(): - # if self.gotemperature_params is not None: - # self.gotemperature_params.set_params(dic) - - # if 'gtemperature_params' in self.__dict__.keys(): - # if self.gotemperature_params is not None: - # self.gotemperature_params.set_params(dic) + if 'gtemperature_params' in self.__dict__.keys(): + if self.gotemperature_params is not None: + self.gotemperature_params.set_params(dic) def print_params(self): """Print all the parameters in the container.""" From a24017a493c155665ec9b2e6cdd9c47409440f13 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 6 Oct 2023 10:52:01 +0200 Subject: [PATCH 069/143] Removing sparsesymbols property Not needed finally --- qgs/params/parameter.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index c74ede8..3d5a78e 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -368,11 +368,6 @@ def symbols(self): symbols[idx] = self[idx].symbol return symbols - @property - def sparsesymbols(self): - """To be implemented""" - pass - @property def input_dimensional(self): """bool: Indicate if the provided value is dimensional or not.""" From 47dcb98a37a434d8242ed3a2b4178fa45bd3b951 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 6 Oct 2023 10:57:00 +0200 Subject: [PATCH 070/143] Cosmetics --- qgs/inner_products/definition.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qgs/inner_products/definition.py b/qgs/inner_products/definition.py index b8cf87c..6dbda72 100644 --- a/qgs/inner_products/definition.py +++ b/qgs/inner_products/definition.py @@ -95,7 +95,7 @@ def ip_lap(self, S, G, symbolic_expr=False): @abstractmethod def ip_diff_x(self, S, G, symbolic_expr=False): - """Function to compute the inner product :math:`(S, \partial_x G)`. + """Function to compute the inner product :math:`(S, \\partial_x G)`. Parameters ---------- @@ -215,7 +215,7 @@ def ip_lap(self, S, G, symbolic_expr=False, integrand=False): return self.symbolic_inner_product(S, self.laplacian(G), symbolic_expr=symbolic_expr, integrand=integrand) def ip_diff_x(self, S, G, symbolic_expr=False, integrand=False): - """Function to compute the inner product :math:`(S, \partial_x G)`. + """Function to compute the inner product :math:`(S, \\partial_x G)`. Parameters ---------- @@ -323,7 +323,7 @@ def jacobian(S, G): .. math: - J(S, G) = \partial_x S\, \partial_y G - \partial_y S\, \partial_x G + J(S, G) = \\partial_x S\\, \\partial_y G - \\partial_y S\\, \\partial_x G Parameters ---------- @@ -359,7 +359,7 @@ def laplacian(S): @staticmethod def integrate_over_domain(expr, symbolic_expr=False): """Definition of the normalized integrals over the spatial domain used by the inner products: - :math:`\\frac{n}{2\\pi^2}\\int_0^\\pi\\int_0^{2\\pi/n} \, \\mathrm{expr}(x, y) \, \\mathrm{d} x \, \\mathrm{d} y`. + :math:`\\frac{n}{2\\pi^2}\\int_0^\\pi\\int_0^{2\\pi/n} \\, \\mathrm{expr}(x, y) \\, \\mathrm{d} x \\, \\mathrm{d} y`. Parameters ---------- @@ -380,7 +380,7 @@ def integrate_over_domain(expr, symbolic_expr=False): def symbolic_inner_product(self, S, G, symbolic_expr=False, integrand=False): """Function defining the inner product to be computed symbolically: - :math:`(S, G) = \\frac{n}{2\\pi^2}\\int_0^\\pi\\int_0^{2\\pi/n} S(x,y)\, G(x,y)\, \\mathrm{d} x \, \\mathrm{d} y`. + :math:`(S, G) = \\frac{n}{2\\pi^2}\\int_0^\\pi\\int_0^{2\\pi/n} S(x,y)\\, G(x,y)\\, \\mathrm{d} x \\, \\mathrm{d} y`. Parameters ---------- @@ -402,4 +402,4 @@ def symbolic_inner_product(self, S, G, symbolic_expr=False, integrand=False): if integrand: return expr, (_x, 0, 2 * pi / _n), (_y, 0, pi) else: - return self.integrate_over_domain(self.optimizer(expr), symbolic_expr=symbolic_expr) \ No newline at end of file + return self.integrate_over_domain(self.optimizer(expr), symbolic_expr=symbolic_expr) From 45ea94503b7ec9c45cc5a38c9b26ea1b5588965e Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 6 Oct 2023 11:00:50 +0200 Subject: [PATCH 071/143] Cosmetics --- qgs/inner_products/symbolic.py | 73 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 3a282e7..7b2ed62 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -36,6 +36,7 @@ # TODO: - Add warnings if trying to connect analytic and symbolic inner products together + class AtmosphericSymbolicInnerProducts(AtmosphericInnerProducts): """Class which contains all the atmospheric inner products coefficients needed for the tendencies tensor :class:`~.tensors.qgtensor.QgsTensor` computation, computed with analytic formula. @@ -262,7 +263,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): args_list = [[(i, j), self.iip.ip_lap, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(noc)] - output = _parallel_compute(pool, args_list, subs, self._d, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._d, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._d = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, noc, output) else: @@ -272,7 +273,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): args_list = [[(i, j), self.iip.symbolic_inner_product, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(noc)] - output = _parallel_compute(pool, args_list, subs, self._s, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._s, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._s = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, noc, output) else: @@ -283,15 +284,14 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.natm) for j in range(noc) for k in range(j, noc) for ell in range(k, noc) for m in range(ell, noc)] - output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not self.mk_subs) elif self._dynamic_T: # v inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.natm) for m in range(noc)] - output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not self.mk_subs) - if self._T4 or self._dynamic_T: if self.return_symbolic: self._v = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, noc, noc, noc, noc)) @@ -357,19 +357,18 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti args_list = [[(i, j), self.iip.symbolic_inner_product, (self._F(i), self._phi(j))] for i in range(self.natm) for j in range(ngr)] - output = _parallel_compute(pool, args_list, subs, self._s, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._s, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._s = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, ngr, output) else: self._s = self._s.to_coo() - # gh inner products if orographic_basis != "atmospheric": args_list = [[(i, j, k), self.iip.ip_jac, (self._F(i), self._F(j), self._phi(k))] for i in range(self.natm) for j in range(self.natm) for k in range(ngr)] - output = _parallel_compute(pool, args_list, subs, self._gh, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._gh, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._gh = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, ngr)) else: @@ -381,13 +380,13 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.natm) for j in range(ngr) for k in range(j, ngr) for ell in range(k, ngr) for m in range(ell, ngr)] - output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not self.mk_subs) elif self._dynamic_T: # v inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.natm) for m in range(ngr)] - output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._v, timeout, permute=True, symbolic_int=not self.mk_subs) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -439,7 +438,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j), self.ip.ip_lap, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - output = _parallel_compute(pool, args_list, subs, self._a, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._a, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._a = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) else: @@ -449,7 +448,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j), self.ip.symbolic_inner_product, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - output = _parallel_compute(pool, args_list, subs, self._u, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._u, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._u = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) else: @@ -459,7 +458,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j), self.ip.ip_diff_x, (self._F(i), self._F(j))] for i in range(self.natm) for j in range(self.natm)] - output = _parallel_compute(pool, args_list, subs, self._c, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._c, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._c = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) else: @@ -469,7 +468,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k), self.ip.ip_jac_lap, (self._F(i), self._F(j), self._F(k))] for i in range(self.natm) for j in range(self.natm) for k in range(self.natm)] - output = _parallel_compute(pool, args_list, subs, self._b, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._b, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._b = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) else: @@ -479,7 +478,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k), self.ip.ip_jac, (self._F(i), self._F(j), self._F(k))] for i in range(self.natm) for j in range(self.natm) for k in range(self.natm)] - output = _parallel_compute(pool, args_list, subs, self._g, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._g, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._g = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) else: @@ -490,13 +489,13 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._F(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.natm) for j in range(self.natm) for k in range(j, self.natm) for ell in range(k, self.natm) for m in range(ell, self.natm)] - output = _parallel_compute(pool, args_list, subs, self._z, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._z, timeout, permute=True, symbolic_int=not self.mk_subs) elif self._dynamic_T: # z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._F(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.natm) for m in range(self.natm)] - output = _parallel_compute(pool, args_list, subs, self._z, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._z, timeout, permute=True, symbolic_int=not self.mk_subs) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -514,7 +513,7 @@ def natm(self): # !-----------------------------------------------------! def _integrate(self, subs, args): - if not(self.mk_subs): + if not self.mk_subs: res = _apply(args) return res[1] @@ -891,9 +890,9 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None # K inner products args_list = [[(i, j), self.iip.ip_lap, (self._phi(i), self._F(j))] for i in range(self.noc) - for j in range(natm)] + for j in range(natm)] - output = _parallel_compute(pool, args_list, subs, self._K, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._K, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._K = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, natm, output) @@ -902,9 +901,9 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None # W inner products args_list = [[(i, j), self.iip.symbolic_inner_product, (self._phi(i), self._F(j))] for i in range(self.noc) - for j in range(natm)] + for j in range(natm)] - output = _parallel_compute(pool, args_list, subs, self._W, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._W, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._W = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, natm, output) @@ -916,13 +915,13 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.noc) for j in range(natm) for k in range(j, natm) for ell in range(k, natm) for m in range(ell, natm)] - output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not self.mk_subs) elif self._dynamic_T: # Z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.noc) for m in range(natm)] - output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not self.mk_subs) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -973,7 +972,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): # N inner products args_list = [[(i, j), self.ip.ip_diff_x, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - output = _parallel_compute(pool, args_list, subs, self._N, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._N, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._N = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) else: @@ -982,7 +981,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): # M inner products args_list = [[(i, j), self.ip.ip_lap, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - output = _parallel_compute(pool, args_list, subs, self._M, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._M, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._M = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) else: @@ -991,7 +990,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): # U inner products args_list = [[(i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j))] for i in range(self.noc) for j in range(self.noc)] - output = _parallel_compute(pool, args_list, subs, self._U, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._U, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._U = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) else: @@ -1001,7 +1000,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k), self.ip.ip_jac, (self._phi(i), self._phi(j), self._phi(k))] for i in range(self.noc) for j in range(self.noc) for k in range(self.noc)] - output = _parallel_compute(pool, args_list, subs, self._O, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._O, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._O = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) else: @@ -1011,7 +1010,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k), self.ip.ip_jac_lap, (self._phi(i), self._phi(j), self._phi(k))] for i in range(self.noc) for j in range(self.noc) for k in range(self.noc)] - output = _parallel_compute(pool, args_list, subs, self._C, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._C, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._C = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) else: @@ -1022,13 +1021,13 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.noc) for j in range(self.noc) for k in range(j, self.noc) for ell in range(k, self.noc) for m in range(ell, self.noc)] - output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not self.mk_subs) elif self._dynamic_T: # V inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.noc) for m in range(self.noc)] - output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not self.mk_subs) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -1372,7 +1371,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None # W inner products args_list = [[(i, j), self.iip.symbolic_inner_product, (self._phi(i), self._F(j))] for i in range(self.ngr) for j in range(natm)] - output = _parallel_compute(pool, args_list, subs, self._W, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._W, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._W = sy.matrices.immutable.ImmutableSparseMatrix(self.ngr, natm, output) else: @@ -1383,13 +1382,13 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(j) * self._F(k) * self._F(ell) * self._F(m))] for i in range(self.ngr) for j in range(natm) for k in range(j, natm) for ell in range(k, natm) for m in range(ell, natm)] - output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not self.mk_subs) elif self._dynamic_T: # Z inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._F(0) * self._F(0) * self._F(0) * self._F(m))] for i in range(self.ngr) for m in range(natm)] - output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._Z, timeout, permute=True, symbolic_int=not self.mk_subs) if self._T4 or self._dynamic_T: if self.return_symbolic: @@ -1431,7 +1430,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): # U inner products args_list = [[(i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j))] for i in range(self.ngr) for j in range(self.ngr)] - output = _parallel_compute(pool, args_list, subs, self._U, timeout, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._U, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: self._U = sy.matrices.immutable.ImmutableSparseMatrix(self.ngr, self.ngr, output) else: @@ -1442,14 +1441,14 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j, k, ell, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j) * self._phi(k) * self._phi(ell) * self._phi(m))] for i in range(self.ngr) for j in range(self.ngr) for k in range(j, self.ngr) for ell in range(k, self.ngr) for m in range(ell, self.ngr)] - output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not self.mk_subs) elif self._dynamic_T: # V inner products args_list = [[(i, 0, 0, 0, m), self.ip.symbolic_inner_product, (self._phi(i), self._phi(0) * self._phi(0) * self._phi(0) * self._phi(m))] for i in range(self.ngr) for m in range(self.ngr)] - output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not(self.mk_subs)) + output = _parallel_compute(pool, args_list, subs, self._V, timeout, permute=True, symbolic_int=not self.mk_subs) if self._T4 or self._dynamic_T: if self.return_symbolic: From e25311412b6a93e70da5243ef3b04b633aded442 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 6 Oct 2023 11:34:04 +0200 Subject: [PATCH 072/143] Cosmetics + Comments --- qgs/functions/symbolic_mul.py | 56 +++--- qgs/tensors/qgtensor.py | 8 +- qgs/tensors/symbolic_qgtensor.py | 319 ++++++++++++++++--------------- 3 files changed, 195 insertions(+), 188 deletions(-) diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py index 6dc5376..cb2eac9 100644 --- a/qgs/functions/symbolic_mul.py +++ b/qgs/functions/symbolic_mul.py @@ -1,10 +1,12 @@ import sympy as sy -def _add_to_dict(dic, loc, value): - ''' - Adds `value` to dictionary `dic`, with the dictionary key of `loc`. + +def add_to_dict(dic, loc, value): + """Adds `value` to dictionary `dic`, with the dictionary key of `loc`. If the dictionary did not have a key of `loc` before, a new key is made. - ''' + + # Jonathan: Add parameters descriptions + """ try: dic[loc] += value @@ -12,9 +14,9 @@ def _add_to_dict(dic, loc, value): dic[loc] = value return dic -def _symbolic_tensordot(a, b, axes=2): - """ - Compute tensor dot product along specified axes of two sympy symbolic arrays + +def symbolic_tensordot(a, b, axes=2): + """Compute tensor dot product along specified axes of two sympy symbolic arrays This is based on numpy.tensordot @@ -45,21 +47,21 @@ def _symbolic_tensordot(a, b, axes=2): return sy.tensorcontraction(prod, sum_cols) + def symbolic_sparse_mult2(dic, vec_a): - """ - Symbolic multiplication of a tensor with one vector: - :math:`A_{i,j} = {\displaystyle \sum_{k=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k} \, a_k` + """Symbolic multiplication of a tensor with one vector: + :math:`A_{i,j} = {\\displaystyle \\sum_{k=0}^{\\mathrm{ndim}}} \\, \\mathcal{T}_{i,j,k} \\, a_k` Parameters ---------- - dic: Dict(Sympy.Symbol) + dic: dict(Sympy.Symbol) A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the tensor. - vec_a: List(Sympy.Symbol) + vec_a: list(Sympy.Symbol) The list :math:`a_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). Returns ------- - res: dict + res: dict # Jonathan: dict of what ? The matrix :math:`A_{i,j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. """ res = dict() @@ -67,14 +69,14 @@ def symbolic_sparse_mult2(dic, vec_a): for key in dic.keys(): coo1, coo2, coo3 = key val = vec_a[coo3] * dic[key] - res = _add_to_dict(res, (coo1, coo2), val) + res = add_to_dict(res, (coo1, coo2), val) return res + def symbolic_sparse_mult3(dic, vec_a, vec_b): - """ - Symbolic multiplication of a tensor with two vectors: - :math:`v_i = {\displaystyle \sum_{j,k=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k} \, a_j \, b_k` + """Symbolic multiplication of a tensor with two vectors: + :math:`v_i = {\\displaystyle \\sum_{j,k=0}^{\\mathrm{ndim}}} \\, \\mathcal{T}_{i,j,k} \\, a_j \\, b_k` Parameters ---------- @@ -97,14 +99,14 @@ def symbolic_sparse_mult3(dic, vec_a, vec_b): for key in dic.keys(): coo1, coo2, coo3 = key val = vec_a[coo2] * vec_b[coo3] * dic[key] - res = _add_to_dict(res, coo1, val) + res = add_to_dict(res, coo1, val) return res + def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): - """ - Symbolic multiplication of a rank-5 tensor with three vectors: - :math:`A_{i, j} = {\displaystyle \sum_{k,l,m=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k,l, m} \, a_k \, b_l \, c_m` + """Symbolic multiplication of a rank-5 tensor with three vectors: + :math:`A_{i, j} = {\\displaystyle \\sum_{k,l,m=0}^{\\mathrm{ndim}}} \\, \\mathcal{T}_{i,j,k,l, m} \\, a_k \\, b_l \\, c_m` Parameters @@ -130,14 +132,14 @@ def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): for key in dic.keys(): coo1, coo2, coo3, coo4, coo5 = key val = vec_a[coo3] * vec_b[coo4] * vec_c[coo5] * dic[key] - res = _add_to_dict(res, (coo1, coo2), val) + res = add_to_dict(res, (coo1, coo2), val) return res + def symbolic_sparse_mult5(dic, vec_a, vec_b, vec_c, vec_d): - """ - Symbolic multiplication of a rank-5 tensor with four vectors: - :math:`v_i = {\displaystyle \sum_{j,k,l,m=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k,l,m} \, a_j \, b_k \, c_l \, d_m` + """Symbolic multiplication of a rank-5 tensor with four vectors: + :math:`v_i = {\\displaystyle \\sum_{j,k,l,m=0}^{\\mathrm{ndim}}} \\, \\mathcal{T}_{i,j,k,l,m} \\, a_j \\, b_k \\, c_l \\, d_m` Parameters ---------- @@ -162,6 +164,6 @@ def symbolic_sparse_mult5(dic, vec_a, vec_b, vec_c, vec_d): for key in dic.keys(): coo1, coo2, coo3, coo4, coo5 = key val = vec_a[coo2] * vec_b[coo3] * vec_c[coo4] * vec_d[coo5] * dic[key] - res = _add_to_dict(res, coo1, val) + res = add_to_dict(res, coo1, val) - return res \ No newline at end of file + return res diff --git a/qgs/tensors/qgtensor.py b/qgs/tensors/qgtensor.py index 7590743..ee7177e 100644 --- a/qgs/tensors/qgtensor.py +++ b/qgs/tensors/qgtensor.py @@ -703,12 +703,12 @@ def jacobian_from_tensor(tensor): Parameters ---------- - tensor: ~sparse.COO + tensor: sparse.COO The qgs tensor. Returns ------- - ~sparse.COO + sparse.COO The Jacobian tensor. """ @@ -729,12 +729,12 @@ def simplify_tensor(tensor): Parameters ---------- - tensor: ~sparse.COO + tensor: sparse.COO The tensor to simplify. Returns ------- - ~sparse.COO + sparse.COO The upper-triangularized tensor. """ coords = tensor.coords.copy() diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 8b4730b..ef120c1 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -5,7 +5,7 @@ This module computes and holds the symbolic representation of the tensors representing the tendencies of the model's equations. """ -from qgs.functions.symbolic_mul import _add_to_dict, _symbolic_tensordot +from qgs.functions.symbolic_mul import add_to_dict, symbolic_tensordot from qgs.params.params import Parameter, ScalingParameter, ParametersArray import numpy as np @@ -15,7 +15,9 @@ from sympy.matrices.immutable import ImmutableSparseMatrix from sympy.tensor.array import ImmutableSparseNDimArray -#//TODO: Check non stored IP version of this +# TODO: Check non stored IP version of this +# Jonathan: All public functions should have a docstring + class SymbolicTensorLinear(object): """Symbolic qgs tendencies tensor class. @@ -66,7 +68,7 @@ def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_p self.tensor = None self.jacobian_tensor = None - if not(self.params.dynamic_T): + if not self.params.dynamic_T: self.compute_tensor() def _psi_a(self, i): @@ -145,8 +147,8 @@ def _deltaT_g(self, i): """ return i + self.params.variables_range[1] + 1 - #//TODO: Im not happy with having these set of properties in two places, one for numerics and one for symbolic. This should be combined, or at least put in the parameter section somewhere. - + # TODO: Im not happy with having these set of properties in two places, one for numerics and one for symbolic. This should be combined, or at least put in the parameter section somewhere. + # Jonathan: Ok I will think about it. Maybe we can add a new properties for parameters to return symbolic expression if it exists. @property def LR(self): if self.params.oceanic_params.gp is None or self.params.oceanic_params.h is None: @@ -326,82 +328,81 @@ def _compute_tensor_dicts(self): if aips.stored and go: # psi_a part - a_inv_mult_c = _symbolic_tensordot(a_inv, aips._c[offset:, offset:], axes=1) + a_inv_mult_c = symbolic_tensordot(a_inv, aips._c[offset:, offset:], axes=1) if gp is not None: hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbols) if gp.orographic_basis == "atmospheric": - a_inv_mult_g = _symbolic_tensordot(a_inv, aips._g[offset:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_inv_mult_g, hk_sym_arr, axes=1) + a_inv_mult_g = symbolic_tensordot(a_inv, aips._g[offset:, offset:, offset:], axes=1) + oro = symbolic_tensordot(a_inv_mult_g, hk_sym_arr, axes=1) else: - a_inv_mult_gh = _symbolic_tensordot(a_inv, aips._gh[offset:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_inv_mult_gh, hk_sym_arr, axes=1) + a_inv_mult_gh = symbolic_tensordot(a_inv, aips._gh[offset:, offset:, offset:], axes=1) + oro = symbolic_tensordot(a_inv_mult_gh, hk_sym_arr, axes=1) - a_inv_mult_b = _symbolic_tensordot(a_inv, aips._b[offset:, offset:, offset:], axes=1) + a_inv_mult_b = symbolic_tensordot(a_inv, aips._b[offset:, offset:, offset:], axes=1) if ocean: a_inv_mult_d = a_inv @ aips._d[offset:, offset:] - for i in range(nvar[0]): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -a_inv_mult_c[i, j] * par.scale_params.beta.symbol) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -(ap.kd.symbol * _kronecker_delta(i, j)) / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd.symbol * _kronecker_delta(i, j)) / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -a_inv_mult_c[i, j] * par.scale_params.beta.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -(ap.kd.symbol * _kronecker_delta(i, j)) / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd.symbol * _kronecker_delta(i, j)) / 2) if gp is not None: # convert if gp.hk is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -oro[i, j] / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j] / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), -oro[i, j] / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro[i, j] / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), self._psi_a(k)), -a_inv_mult_b[i, j, k]) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), self._theta_a(ko)), -a_inv_mult_b[i, j, k]) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), self._psi_a(k)), -a_inv_mult_b[i, j, k]) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), self._theta_a(ko)), -a_inv_mult_b[i, j, k]) if ocean: for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * ap.kd.symbol / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * ap.kd.symbol / 2) # theta_a part - a_theta_mult_u = _symbolic_tensordot(a_theta, aips._u, axes=1) + a_theta_mult_u = symbolic_tensordot(a_theta, aips._u, axes=1) if self.Cpa is not None: - val_Cpa = _symbolic_tensordot(a_theta_mult_u , self.Cpa, axes=1) + val_Cpa = symbolic_tensordot(a_theta_mult_u, self.Cpa, axes=1) if atp.hd is not None and atp.thetas is not None: thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbols) - val_thetas = _symbolic_tensordot(a_theta_mult_u, thetas_sym_arr, axes=1) # not perfect + val_thetas = symbolic_tensordot(a_theta_mult_u, thetas_sym_arr, axes=1) # not perfect - a_theta_mult_a = _symbolic_tensordot(a_theta, aips._a[:, offset:], axes=1) - a_theta_mult_c = _symbolic_tensordot(a_theta, aips._c[:, offset:], axes=1) + a_theta_mult_a = symbolic_tensordot(a_theta, aips._a[:, offset:], axes=1) + a_theta_mult_c = symbolic_tensordot(a_theta, aips._c[:, offset:], axes=1) - a_theta_mult_g = _symbolic_tensordot(a_theta, aips._g[:, offset:, offset:], axes=1) + a_theta_mult_g = symbolic_tensordot(a_theta, aips._g[:, offset:, offset:], axes=1) if gp is not None: if gp.orographic_basis == "atmospheric": - oro = _symbolic_tensordot(a_theta_mult_g, hk_sym_arr, axes=1) + oro = symbolic_tensordot(a_theta_mult_g, hk_sym_arr, axes=1) else: - a_theta_mult_gh = _symbolic_tensordot(a_theta, aips._gh[:, offset:, offset:], axes=1) - oro = _symbolic_tensordot(a_theta_mult_gh, hk_sym_arr, axes=1) + a_theta_mult_gh = symbolic_tensordot(a_theta, aips._gh[:, offset:, offset:], axes=1) + oro = symbolic_tensordot(a_theta_mult_gh, hk_sym_arr, axes=1) - a_theta_mult_b = _symbolic_tensordot(a_theta, aips._b[:, offset:, offset:], axes=1) + a_theta_mult_b = symbolic_tensordot(a_theta, aips._b[:, offset:, offset:], axes=1) if ocean: - a_theta_mult_d = _symbolic_tensordot(a_theta, aips._d[:, offset:], axes=1) - a_theta_mult_s = _symbolic_tensordot(a_theta, aips._s, axes=1) + a_theta_mult_d = symbolic_tensordot(a_theta, aips._d[:, offset:], axes=1) + a_theta_mult_s = symbolic_tensordot(a_theta, aips._s, axes=1) if ground_temp: - a_theta_mult_s = _symbolic_tensordot(a_theta, aips._s, axes=1) + a_theta_mult_s = symbolic_tensordot(a_theta, aips._s, axes=1) for i in range(nvar[1]): if self.Cpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i]) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i]) if atp.hd is not None and atp.thetas is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_thetas[i] * atp.hd.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_thetas[i] * atp.hd.symbol) for j in range(nvar[0]): @@ -409,111 +410,111 @@ def _compute_tensor_dicts(self): val_2 = a_theta_mult_a[i, j] * ap.kd.symbol * ap.sig0.symbol / 2 val_3 = a_theta_mult_a[i, j] * (ap.kd.symbol / 2 + 2 * ap.kdp.symbol) * ap.sig0.symbol - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val_2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -val_3) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val_2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -val_3) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -a_theta_mult_c[i, j] * par.scale_params.beta.symbol * ap.sig0.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -a_theta_mult_c[i, j] * par.scale_params.beta.symbol * ap.sig0.symbol) if gp is not None: if gp.hk is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -ap.sig0.symbol * oro[i, j] / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro[i, j] / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), -ap.sig0.symbol * oro[i, j] / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro[i, j] / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - a_theta_mult_b[i, j, k] * ap.sig0.symbol) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - a_theta_mult_b[i, j, k] * ap.sig0.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - a_theta_mult_b[i, j, k] * ap.sig0.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - a_theta_mult_b[i, j, k] * ap.sig0.symbol) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), a_theta_mult_g[i, j, k]) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), a_theta_mult_g[i, j, k]) for j in range(nvar[1]): if self.Lpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.sc.symbol * self.Lpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.sc.symbol * self.Lpa) if self.LSBpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * self.LSBpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * self.LSBpa) if atp.hd is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.hd) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.hd) if ocean: for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), -a_theta_mult_d[i, j] * ap.sig0.symbol * ap.kd.symbol / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), -a_theta_mult_d[i, j] * ap.sig0.symbol * ap.kd.symbol / 2) if self.Lpa is not None: for j in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.Lpa / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.Lpa / 2) if self.LSBpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.LSBpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.LSBpgo) if ground_temp: if self.Lpa is not None: for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.Lpa / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.Lpa / 2) if self.LSBpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.LSBpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.LSBpgo) if ocean: # psi_o part - M_psio_mult_K = _symbolic_tensordot(M_psio, bips._K[offset:, offset:], axes=1) - M_psio_mult_N = _symbolic_tensordot(M_psio, bips._N[offset:, offset:], axes=1) - M_psio_mult_M = _symbolic_tensordot(M_psio, bips._M[offset:, offset:], axes=1) - M_psio_mult_C = _symbolic_tensordot(M_psio, bips._C[offset:, offset:, offset:], axes=1) + M_psio_mult_K = symbolic_tensordot(M_psio, bips._K[offset:, offset:], axes=1) + M_psio_mult_N = symbolic_tensordot(M_psio, bips._N[offset:, offset:], axes=1) + M_psio_mult_M = symbolic_tensordot(M_psio, bips._M[offset:, offset:], axes=1) + M_psio_mult_C = symbolic_tensordot(M_psio, bips._C[offset:, offset:, offset:], axes=1) for i in range(nvar[2]): for j in range(nvar[0]): jo = j + offset # skipping the theta 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), M_psio_mult_K[i, j] * par.oceanic_params.d.symbol) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), -M_psio_mult_K[i, j] * par.oceanic_params.d.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), M_psio_mult_K[i, j] * par.oceanic_params.d.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), -M_psio_mult_K[i, j] * par.oceanic_params.d.symbol) for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_N[i, j] * par.scale_params.beta.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_N[i, j] * par.scale_params.beta.symbol) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_M[i, j] * (par.oceanic_params.r.symbol + par.oceanic_params.d.symbol)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), -M_psio_mult_M[i, j] * (par.oceanic_params.r.symbol + par.oceanic_params.d.symbol)) for k in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio_mult_C[i, j, k]) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio_mult_C[i, j, k]) # deltaT_o part - U_inv_mult_W = _symbolic_tensordot(U_inv, bips._W, axes=1) - U_inv_mult_W_Cpgo = _symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) + U_inv_mult_W = symbolic_tensordot(U_inv, bips._W, axes=1) + U_inv_mult_W_Cpgo = symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) - U_inv_mult_O = _symbolic_tensordot(U_inv, bips._O[:, offset:, offset:], axes=1) + U_inv_mult_O = symbolic_tensordot(U_inv, bips._O[:, offset:, offset:], axes=1) for i in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i]) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i]) for j in range(nvar[1]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.Lpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.Lpgo) if self.sbpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) for j in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) if self.sbpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.sbpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.sbpgo * _kronecker_delta(i, j)) for j in range(nvar[2]): for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(ko)), -U_inv_mult_O[i, j, k]) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(ko)), -U_inv_mult_O[i, j, k]) # deltaT_g part if ground_temp: - U_inv_mult_W = _symbolic_tensordot(U_inv, bips._W, axes=1) - U_inv_mult_W_Cpgo = _symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) + U_inv_mult_W = symbolic_tensordot(U_inv, bips._W, axes=1) + U_inv_mult_W_Cpgo = symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) for i in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i]) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i]) for j in range(nvar[1]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.Lpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.Lpgo) if self.sbpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) if self.sbpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.sbpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.sbpgo * _kronecker_delta(i, j)) else: # psi_a part @@ -525,9 +526,9 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.c(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - val * par.scale_params.beta.symbol) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - (ap.kd.symbol * _kronecker_delta(i, j)) / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd.symbol * _kronecker_delta(i, j)) / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - val * par.scale_params.beta.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - (ap.kd.symbol * _kronecker_delta(i, j)) / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), (ap.kd.symbol * _kronecker_delta(i, j)) / 2) if gp is not None: hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbols) @@ -541,24 +542,23 @@ def _compute_tensor_dicts(self): for jj in range(nvar[0]): for kk in range(nvar[0]): oro += a_inv[i, jj] * aips.gh(offset + jj, j, offset + kk) * hk_sym_arr[kk] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - oro / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), 0), - oro / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), 0), oro / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.b(offset + jj, jo, ko) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), self._psi_a(k)), - val) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), self._theta_a(ko)), - val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_a(j), self._psi_a(k)), - val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._theta_a(jo), self._theta_a(ko)), - val) if ocean: for j in range(nvar[2]): jo = j + offset val = 0 for jj in range(nvar[0]): val += a_inv[i, jj] * aips.d(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), val * ap.kd.symbol / 2) - + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), val * ap.kd.symbol / 2) # theta_a part for i in range(nvar[1]): @@ -568,7 +568,7 @@ def _compute_tensor_dicts(self): for kk in range(nvar[1]): val -= a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val) if atp.hd is not None and atp.thetas is not None: thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbols) @@ -577,7 +577,7 @@ def _compute_tensor_dicts(self): for kk in range(nvar[1]): val -= a_theta[i, jj] * aips.u(jj, kk) * thetas_sym_arr[kk] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val * atp.hd.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val * atp.hd.symbol) for j in range(nvar[0]): @@ -587,14 +587,14 @@ def _compute_tensor_dicts(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips.a(jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val * ap.kd.symbol * ap.sig0.symbol / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - val * (ap.kd.symbol / 2 - 2 * ap.kdp.symbol) * ap.sig0.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), val * ap.kd.symbol * ap.sig0.symbol / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - val * (ap.kd.symbol / 2 - 2 * ap.kdp.symbol) * ap.sig0.symbol) val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.c(jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), val * par.scale_params.beta.symbol * ap.sig0.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), val * par.scale_params.beta.symbol * ap.sig0.symbol) if gp is not None: if gp.hk is not None: @@ -607,8 +607,8 @@ def _compute_tensor_dicts(self): for jj in range(nvar[1]): for kk in range(nvar[0]): oro += a_theta[i, jj] * aips.gh(jj, jo, offset + kk) * gp.hk[kk].symbol - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - ap.sig0.symbol * oro / 2) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), 0), - ap.sig0.symbol * oro / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), 0), ap.sig0.symbol * oro / 2) for k in range(nvar[0]): ko = k + offset # skipping the theta 0 variable if it exists @@ -616,14 +616,14 @@ def _compute_tensor_dicts(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips.b(jj, jo, ko) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - val * ap.sig0.symbol) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - val * ap.sig0.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), - val * ap.sig0.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(jo), self._psi_a(k)), - val * ap.sig0.symbol) val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.g(jj, jo, ko) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), val) for j in range(nvar[1]): val = 0 @@ -631,13 +631,13 @@ def _compute_tensor_dicts(self): val += a_theta[i, jj] * aips.u(jj, j) if self.Lpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.sc.symbol * self.Lpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.sc.symbol * self.Lpa) if self.LSBpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.LSBpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.LSBpa) if atp.hd is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.hd.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.hd.symbol) if ocean: for j in range(nvar[2]): @@ -646,16 +646,16 @@ def _compute_tensor_dicts(self): for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.d(jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * ap.sig0.symbol * ap.kd.symbol / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * ap.sig0.symbol * ap.kd.symbol / 2) if self.Lpa is not None: for j in range(nvar[3]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.Lpa / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.Lpa / 2) if self.LSBpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.LSBpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.LSBpgo) if ground_temp: if self.Lpa is not None: @@ -663,9 +663,9 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.Lpa / 2) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.Lpa / 2) if self.LSBpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.LSBpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.LSBpgo) if ocean: # psi_o part @@ -675,8 +675,8 @@ def _compute_tensor_dicts(self): for jj in range(nvar[2]): val = M_psio[i, jj] * bips.K(offset + jj, jo) * par.oceanic_params.d.symbol - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), val) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), - val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_a(j), 0), val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._theta_a(jo), 0), - val) for j in range(nvar[2]): jo = j + offset # skipping the T 0 variable if it exists @@ -684,19 +684,19 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.N(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * par.scale_params.beta.symbol) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * par.scale_params.beta.symbol) val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.M(offset + jj, jo) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * (par.oceanic_params.r.symbol + par.oceanic_params.d.symbol)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), 0), val * (par.oceanic_params.r.symbol + par.oceanic_params.d.symbol)) for k in range(nvar[2]): ko = k + offset # skipping the T 0 variable if it exists val = 0 for jj in range(nvar[2]): val -= M_psio[i, jj] * bips.C(offset + jj, jo, ko) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), val) # deltaT_o part for i in range(nvar[3]): @@ -705,20 +705,20 @@ def _compute_tensor_dicts(self): for kk in range(nvar[3]): val += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), val) for j in range(nvar[1]): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips.W(jj, j) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) if self.sbpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * self.sbpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * self.sbpa) for j in range(nvar[3]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) if self.sbpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.sbpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.sbpgo * _kronecker_delta(i, j)) for j in range(nvar[2]): for k in range(offset, nvar[3]): @@ -727,7 +727,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[3]): val -= U_inv[i, jj] * bips.O(jj, jo, k) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(k)), val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._psi_o(j), self._deltaT_o(k)), val) # deltaT_g part if ground_temp: @@ -737,21 +737,21 @@ def _compute_tensor_dicts(self): for kk in range(nvar[2]): val += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), val) for j in range(nvar[1]): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips.W(jj, j) - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) if self.sbpa is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.sbpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.sbpa) for j in range(nvar[2]): - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) if self.sbpgo is not None: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.sbpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.sbpgo * _kronecker_delta(i, j)) return sy_arr_dic @@ -812,7 +812,7 @@ def jacobian_from_dict(dic): new_pos[i+1] = orig_order[1] for key in keys: - dic_jac = _add_to_dict(dic_jac, tuple(key[i] for i in new_pos), dic[key]) + dic_jac = add_to_dict(dic_jac, tuple(key[i] for i in new_pos), dic[key]) return dic_jac @@ -823,7 +823,7 @@ def simplify_dict(dic): for key in keys: new_key = tuple([key[0]] + sorted(key[1:])) - dic_upp = _add_to_dict(dic_upp, new_key, dic[key]) + dic_upp = add_to_dict(dic_upp, new_key, dic[key]) return dic_upp @@ -842,15 +842,14 @@ def save_to_file(self, filename, **kwargs): f.close() def sub_tensor(self, tensor=None, continuation_variables=list()): - """ - Uses sympy substitution to convert the symbolic tensor or a symbolic dictionary to a numerical one. + """Uses sympy substitution to convert the symbolic tensor or a symbolic dictionary to a numerical one. Parameters ---------- - tensor: dict, sympy array + tensor: dict, sympy array # Jonathan: be more precise here continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) - if None all variables are substituted. This variable is the opposite of 'variables' + If None all variables are substituted. This variable is the opposite of 'variables'. # Jonathan: what does it means? Returns ------- @@ -875,21 +874,23 @@ def sub_tensor(self, tensor=None, continuation_variables=list()): ten_out[key] = val else: - #Assuming the tensor is a sympy tensor + # Assuming the tensor is a sympy tensor ten_out = ten.subs(param_subs) - return ten_out def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): - ''' - Print the non-zero coordinates of values of the tensor of the model tendencies + """Print the non-zero coordinates of values of the tensor of the model tendencies Parameters ---------- tensor: dict or Sympy ImmutableSparseNDimArray Tensor of model tendencies, either as a dictionary with keys of non-zero coordinates, and values of Sympy Symbols or floats, or as a ImmutableSparseNDimArray. - ''' + dict_opp: bool + ... + tol: float + ... + """ if tensor is None: if dict_opp: temp_ten = self.tensor_dic @@ -919,8 +920,8 @@ def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): output_val = v print(str(ix) + ": " + str(output_val)) -class SymbolicTensorDynamicT(SymbolicTensorLinear): +class SymbolicTensorDynamicT(SymbolicTensorLinear): """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. Parameters @@ -1041,7 +1042,6 @@ def _compute_stored_full_dict(self): sy_arr_dic = dict() # theta_a part - val = 0 for i in range(nvar[1]): if self.T4LSBpa is not None: j = k = ell = 0 @@ -1050,9 +1050,9 @@ def _compute_stored_full_dict(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips._z[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4LSBpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4LSBpa * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4LSBpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4LSBpa * val) if ocean: if self.T4LSBpgo is not None: @@ -1062,9 +1062,9 @@ def _compute_stored_full_dict(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips._v[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -self.T4LSBpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -self.T4LSBpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.T4LSBpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.T4LSBpgo * val) if ground_temp: if self.T4LSBpgo is not None: @@ -1074,12 +1074,12 @@ def _compute_stored_full_dict(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips._v[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.T4LSBpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.T4LSBpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.T4LSBpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.T4LSBpgo * val) if ocean: - #delta_T part + # delta_T part for i in range(nvar[3]): j = k = ell = 0 for m in range(nvar[1]): @@ -1087,18 +1087,18 @@ def _compute_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) for m in range(nvar[3]): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), - self.T4sbpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), - self.T4sbpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.T4sbpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.T4sbpgo * val) if ground_temp: # deltaT_g part @@ -1109,18 +1109,18 @@ def _compute_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) for m in range(nvar[2]): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.T4sbpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.T4sbpgo * val) else: - sy_arr_dic = _add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.T4sbpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.T4sbpgo * val) return sy_arr_dic @@ -1169,7 +1169,6 @@ def _compute_non_stored_full_dict(self): U_inv = U_inv.inverse() - ################# sy_arr_dic = dict() @@ -1264,7 +1263,7 @@ def compute_tensor(self): """Routine to compute the tensor.""" # gathering if self.params.T4: - #//TODO: Make a proper error message for here + # TODO: Make a proper error message for here raise ValueError("Parameters are set for T4 version, set dynamic_T=True") symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) @@ -1278,8 +1277,10 @@ def compute_tensor(self): if symbolic_dict_dynT is not None: self._set_tensor(symbolic_dict_dynT) + class SymbolicTensorT4(SymbolicTensorLinear): # TODO: this takes a long time (>1hr) to run. I think we need a better way to run the non-stored z, v, Z, V IPs. Maybe do not allow `n` as a continuation parameter for this version? + # Jonathan: Could be done by raising an error if so. """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. Parameters @@ -1367,7 +1368,6 @@ def _compute_non_stored_full_dict(self): U_inv = U_inv.inverse() - ################# sy_arr_dic = dict() @@ -1405,7 +1405,7 @@ def _compute_non_stored_full_dict(self): for m in range(nvar[2]): val = 0 for jj in range(nvar[1]): - val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) + val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4LSBpgo * val @@ -1461,7 +1461,7 @@ def _compute_non_stored_full_dict(self): def compute_tensor(self): """Routine to compute the tensor.""" # gathering - if not(self.params.T4): + if not self.params.T4: raise ValueError("Parameters are not set for T4 version") symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) @@ -1484,6 +1484,7 @@ def _kronecker_delta(i, j): else: return 0 + def _shift_dict_keys(dic, shift): """ Keys of given dictionary are altered to add values in the given indicies @@ -1502,6 +1503,7 @@ def _shift_dict_keys(dic, shift): return shifted_dic + def _parameter_substitutions(params, continuation_varaibles): subs = _parameter_values(params) @@ -1510,6 +1512,7 @@ def _parameter_substitutions(params, continuation_varaibles): subs.update(_parameter_values(params.scale_params)) # TODO: Is there a better way to do this which is dynamic, the __dict__ method doesnt include the properties? + # Jonathan: I don't understand # Manually add properties from class subs[params.scale_params.L.symbol] = params.scale_params.L subs[params.scale_params.beta.symbol] = params.scale_params.beta @@ -1544,9 +1547,10 @@ def _parameter_substitutions(params, continuation_varaibles): return subs + def _parameter_values(pars): """ - Function takes a parameter class and produces a dictionary of the symbol and the corrisponding numerical value + Function takes a parameter class and produces a dictionary of the symbol and the corresponding numerical value """ subs = dict() @@ -1564,9 +1568,10 @@ def _parameter_values(pars): if v.symbol is not None or v.symbol != 0: subs[v.symbol] = v return subs - + + if __name__ == "__main__": dic = dict() - dic = _add_to_dict(dic, (0, 0), 1) - dic = _add_to_dict(dic, (0, 0), 2) - print(dic) \ No newline at end of file + dic = add_to_dict(dic, (0, 0), 1) + dic = add_to_dict(dic, (0, 0), 2) + print(dic) From 2ada3c57811039887fe9b43ecbc59447277028df Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 9 Oct 2023 18:13:35 +0200 Subject: [PATCH 073/143] Continued cosmetics + comments --- qgs/functions/sparse_mul.py | 10 +- qgs/functions/symbolic_mul.py | 2 - qgs/functions/symbolic_output.py | 180 ++++++++++++++++--------------- qgs/inner_products/definition.py | 2 +- 4 files changed, 102 insertions(+), 92 deletions(-) diff --git a/qgs/functions/sparse_mul.py b/qgs/functions/sparse_mul.py index cb2707e..22ada30 100644 --- a/qgs/functions/sparse_mul.py +++ b/qgs/functions/sparse_mul.py @@ -9,10 +9,11 @@ import numpy as np from numba import njit + @njit def sparse_mul2(coo, value, vec): """Sparse multiplication of a tensor with one vector: - :math:`A_{i,j} = {\displaystyle \sum_{k=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k} \, a_k` + :math:`A_{i,j} = {\\displaystyle \\sum_{k=0}^{\\mathrm{ndim}}} \\, \\mathcal{T}_{i,j,k} \\, a_k` Warnings -------- @@ -47,7 +48,7 @@ def sparse_mul2(coo, value, vec): @njit def sparse_mul3(coo, value, vec_a, vec_b): """Sparse multiplication of a tensor with two vectors: - :math:`v_i = {\displaystyle \sum_{j,k=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k} \, a_j \, b_k` + :math:`v_i = {\\displaystyle \\sum_{j,k=0}^{\\mathrm{ndim}}} \\, \\mathcal{T}_{i,j,k} \\, a_j \\, b_k` Warnings -------- @@ -79,10 +80,11 @@ def sparse_mul3(coo, value, vec_a, vec_b): res[0] = 1. return res + @njit def sparse_mul4(coo, value, vec_a, vec_b, vec_c): """Sparse multiplication of a rank-5 tensor with three vectors: - :math:`A_{i, j} = {\displaystyle \sum_{k,l,m=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k,l, m} \, a_k \, b_l \, c_m` + :math:`A_{i, j} = {\\displaystyle \\sum_{k,l,m=0}^{\\mathrm{ndim}}} \\, \\mathcal{T}_{i,j,k,l, m} \\, a_k \\, b_l \\, c_m` Warnings -------- @@ -119,7 +121,7 @@ def sparse_mul4(coo, value, vec_a, vec_b, vec_c): @njit def sparse_mul5(coo, value, vec_a, vec_b, vec_c, vec_d): """Sparse multiplication of a rank-5 tensor with four vectors: - :math:`v_i = {\displaystyle \sum_{j,k,l,m=0}^{\mathrm{ndim}}} \, \mathcal{T}_{i,j,k,l,m} \, a_j \, b_k \, c_l \, d_m` + :math:`v_i = {\\displaystyle \\sum_{j,k,l,m=0}^{\\mathrm{ndim}}} \\, \\mathcal{T}_{i,j,k,l,m} \\, a_j \\, b_k \\, c_l \\, d_m` Warnings -------- diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py index cb2eac9..ed47a61 100644 --- a/qgs/functions/symbolic_mul.py +++ b/qgs/functions/symbolic_mul.py @@ -113,8 +113,6 @@ def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): ---------- dic: Dict(Sympy.Symbol) A dictionary where they keys are a tuple of 5 elements which are the coordinates of the tensor values, which are contained in the dictionary values. - value: ~numpy.ndarray(float) - A 1D array of shape (n_elems,), a list of value in the tensor vec_a: List(Sympy.Symbol) The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). vec_b: List(Sympy.Symbol) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 2097f70..035e06f 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -1,5 +1,7 @@ +# Jonathan: Needs a module description here + import numpy as np -import sympy as sy +from sympy import Symbol, lambdify import warnings from qgs.functions.symbolic_mul import symbolic_sparse_mult2, symbolic_sparse_mult3, symbolic_sparse_mult4, symbolic_sparse_mult5 @@ -15,7 +17,7 @@ fortran_lang_translation = { '**': '^' - #TODO: may need to add variable for pi + # TODO: may need to add variable for pi } julia_lang_translation = { @@ -28,13 +30,13 @@ '**': '^' } -def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables=list(), language='python', return_inner_products=False, return_jacobian=False, return_symbolic_eqs=False, return_symbolic_qgtensor=False): - """ - Function to output the raw symbolic functions of the qgs model. + +def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables=None, language='python', return_inner_products=False, return_jacobian=False, return_symbolic_eqs=False, return_symbolic_qgtensor=False): + """Function to output the raw symbolic functions of the qgs model. Parameters ---------- - params: QGParams + params: QgParams The parameters fully specifying the model configuration. atm_ip: SymbolicAtmosphericInnerProducts, optional Allows for stored inner products to be input @@ -44,29 +46,29 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con Allows for stored inner products to be input continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) The variables to not substitute and to leave in the equations, if None no variables are substituted - language: String + language: str Options for the output language syntax: 'python', 'julia', 'fortran', 'auto', 'mathematica' return_inner_products: bool - If True, return the inner products of the model. Default to False. + If `True`, return the inner products of the model. Default to `False`. return_jacobian: bool - If True, return the Jacobian of the model. Default to False. + If `True`, return the Jacobian of the model. Default to `False`. return_symbolic_eqs: bool - If True, return the substituted symbolic equations + If `True`, return the substituted symbolic equations return_symbolic_qgtensor: bool - If True, return the symbolic tendencies tensor of the model. Default to False. + If `True`, return the symbolic tendencies tensor of the model. Default to `False`. Returns ------- - funcs: string + funcs: str The substituted functions in the language syntax specified, as a string - Deq_simplified: symbolic equations - Dict of the substituted Jacobian matrix - inner_products: (SymbolicAtmosphericInnerProducts, SymbolicOceanicInnerProducts, SymbolicGroundInnerProducts) - If `return_inner_products` is True, the inner products of the system. + dict_eq_simplified: symbolic equations + Dictionary of the substituted Jacobian matrix + inner_products: tuple(SymbolicAtmosphericInnerProducts, SymbolicOceanicInnerProducts, SymbolicGroundInnerProducts) + If `return_inner_products` is `True`, the inner products of the system. eq_simplified: Symbolic equations - If `return_symbolic_eqs` is True, Dict of the model tendencies symbolic functions + If `return_symbolic_eqs` is `True`, dictionary of the model tendencies symbolic functions. agotensor: SymbolicQgsTensor - If `return_symbolic_qgtensor` is True, the symbolic tendencies tensor of the system. + If `return_symbolic_qgtensor` is `True`, the symbolic tendencies tensor of the system. """ make_ip_subs = True @@ -76,19 +78,19 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con else: for cv in continuation_variables: try: - if params.scale_params.n == cv: + if params.scale_params.n == cv: make_ip_subs = False except: pass - - if not(make_ip_subs): - warnings.warn("Calculating innerproducts symbolically, as the variable 'n' has been specified as a variable, this takes several minutes.") + if not make_ip_subs: + warnings.warn("Calculating inner products symbolically, as the variable 'n' has been specified as a variable, this takes several minutes.") if params.atmospheric_basis is not None: if atm_ip is None: aip = AtmosphericSymbolicInnerProducts(params, return_symbolic=True, make_substitution=make_ip_subs) - else: aip = atm_ip + else: + aip = atm_ip else: aip = None @@ -126,20 +128,20 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con xx.append(1) for i in range(1, params.ndim+1): - xx.append(sy.Symbol('U_'+str(i))) + xx.append(Symbol('U_'+str(i))) if params.dynamic_T: eq = symbolic_sparse_mult5(agotensor.sub_tensor(continuation_variables=continuation_variables), xx, xx, xx, xx) if return_jacobian: - Deq = symbolic_sparse_mult4(agotensor.sub_tensor(agotensor.jac_dic, continuation_variables=continuation_variables), xx, xx, xx) + dict_eq = symbolic_sparse_mult4(agotensor.sub_tensor(agotensor.jac_dic, continuation_variables=continuation_variables), xx, xx, xx) else: eq = symbolic_sparse_mult3(agotensor.sub_tensor(continuation_variables=continuation_variables), xx, xx) if return_jacobian: - Deq = symbolic_sparse_mult2(agotensor.sub_tensor(agotensor.jac_dic, continuation_variables=continuation_variables), xx) + dict_eq = symbolic_sparse_mult2(agotensor.sub_tensor(agotensor.jac_dic, continuation_variables=continuation_variables), xx) eq_simplified = dict() - Deq_simplified = dict() + dict_eq_simplified = dict() if continuation_variables is None: # Simplifying at this step is slow @@ -148,20 +150,20 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con eq_simplified[i] = eq[i].simplify() if return_jacobian: for j in range(1, params.ndim+1): - if (i, j) in Deq: - Deq_simplified[(i, j)] = Deq[(i, j)].simplify() + if (i, j) in dict_eq: + dict_eq_simplified[(i, j)] = dict_eq[(i, j)].simplify() else: eq_simplified = eq if return_jacobian: - Deq_simplified = Deq + dict_eq_simplified = dict_eq func = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, continuation_variables=continuation_variables) ret = list() ret.append('\n'.join(func)) if return_jacobian: - ret.append(Deq_simplified) + ret.append(dict_eq_simplified) if return_inner_products: ret.append((aip, oip, gip)) if return_symbolic_eqs: @@ -170,9 +172,9 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con ret.append(agotensor) return ret + def translate_equations(equations, language='python'): - ''' - Function to output the model equations as a string in the specified language syntax. + """Function to output the model equations as a string in the specified language syntax. Parameters ---------- @@ -185,12 +187,13 @@ def translate_equations(equations, language='python'): - `julia` - `auto` - `mathematica` + Default to `python`. Returns ------- str_eq: dict - dict of strings of the model equations - ''' + Dictionary of strings of the model equations. + """ if language == 'python': translator = python_lang_translation @@ -206,7 +209,7 @@ def translate_equations(equations, language='python'): str_eq = dict() for key in equations.keys(): temp_str = str(equations[key]) - #//TODO: This only works for single array, not jacobian + # TODO: This only works for single array, not jacobian for k in translator.keys(): temp_str = temp_str.replace(k, translator[k]) str_eq[key] = temp_str @@ -218,16 +221,16 @@ def translate_equations(equations, language='python'): return str_eq + def format_equations(equations, params, save_loc=None, language='python', print_equations=False): - ''' - Function formats the equations, in the programming language specified, and saves the equations to the specified location. + """Function formats the equations, in the programming language specified, and saves the equations to the specified location. The variables in the equation are substituted if the model variable is input. Parameters ---------- - equations: Dict + equations: dict Dictionary of symbolic model equations - params: QGParams + params: QgParams qgs model params save_loc: String location to save the outputs as a .txt file @@ -238,39 +241,38 @@ def format_equations(equations, params, save_loc=None, language='python', print_ - `julia` - `auto` - `mathematica` - free_variables: Set or list or None - The variables to not substitute and to leave in the equations, if None no variables are substituted + Default to `python`. print_equations: bool - If True, equations are printed by the function, if False, equation string is returned by the function. Defaults to False + If `True`, equations are printed by the function, if `False`, equation string is returned by the function. Defaults to `False` Returns ------- - equation_dict: Dict + equation_dict: dict Dictionary of symbolic model equations, that have been substituted with numerical values free_vars: Set - Set of strings of model variables that have not been substitued in this function, and remain as variabes in the equaitons. + Set of strings of model variables that have not been substituted in this function, and remain as variables in the equations. - ''' + """ equation_dict = dict() # Substitute variable symbols vector_subs = dict() if language == 'python': for i in range(1, params.ndim+1): - vector_subs['U_'+str(i)] = sy.Symbol('U['+str(i-1)+']') + vector_subs['U_'+str(i)] = Symbol('U['+str(i-1)+']') if language == 'fortran' or language == 'auto': for i in range(1, params.ndim+1): - vector_subs['U_'+str(i)] = sy.Symbol('U('+str(i)+')') + vector_subs['U_'+str(i)] = Symbol('U('+str(i)+')') if language == 'julia': for i in range(1, params.ndim+1): - vector_subs['U_'+str(i)] = sy.Symbol('U['+str(i)+']') + vector_subs['U_'+str(i)] = Symbol('U['+str(i)+']') if language == 'mathematica': for i in range(1, params.ndim+1): - vector_subs['U_'+str(i)] = sy.Symbol('U('+str(i)+')') + vector_subs['U_'+str(i)] = Symbol('U('+str(i)+')') for k in equations.keys(): eq = equations[k].subs(vector_subs) @@ -294,28 +296,35 @@ def format_equations(equations, params, save_loc=None, language='python', print_ else: return equation_dict -def equation_as_function(equations, params, string_output=True, language='python', continuation_variables=list()): - ''' - Converts the symbolic equations to a function in string format in the language syntax specified, or a lambdified python function + +def equation_as_function(equations, params, string_output=True, language='python', continuation_variables=None): + """Converts the symbolic equations to a function in string format in the language syntax specified, or a lambdified python function Parameters ---------- - equations: Dict + equations: dict Dictionary of the substituted symbolic model equations - params: QGParams + params: QgParams The parameters fully specifying the model configuration. string_output: bool - If True, returns a lambdified python function, if False returns a string function, defaults to True - free_variables: Set or List or None + If `True`, returns a lambdified python function, if `False` returns a string function. Defaults to `True` + language: string + Language syntax that the equations are returned in. Options are: + - `python` + - `fortran` + - `julia` + - `auto` + - `mathematica` + Default to `python`. + continuation_variables: Set or list or None Variables that are not substituted with numerical values. If None, no symbols are substituted - Returns ------- - f_output: lambdified python function, or String - If string_output is True, output is a funciton in the specified language syntax, if False the output is a lambdified python function + f_output: callable or str + If string_output is `True`, output is a function in the specified language syntax, if `False` the output is a lambdified python function - ''' + """ eq_dict = format_equations(equations, params, language=language) @@ -335,15 +344,15 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('\treturn F') else: - # Return a lamdafied function - vec = [sy.Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] + # Return a lambdified function + vec = [Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] array_eqs = np.array(list(eq_dict.values())) inputs = ['t', vec] for v in continuation_variables: inputs.append(v.symbol) - f_output = sy.lambdify(inputs, array_eqs) + f_output = lambdify(inputs, array_eqs) if language == 'julia': eq_dict = translate_equations(eq_dict, language='julia') @@ -391,7 +400,7 @@ def equation_as_function(equations, params, string_output=True, language='python create_auto_file(eq_dict, params, continuation_variables) if language == 'mathematica': - #TODO: This function needs testing before release + # TODO: This function needs testing before release eq_dict = translate_equations(eq_dict, language='mathematica') f_output.append('F = Array[' + str(len(eq_dict)) + ']') @@ -399,34 +408,34 @@ def equation_as_function(equations, params, string_output=True, language='python for n, eq in enumerate(eq_dict.values()): f_output.append('F['+str(n+1)+'] = ' + str(eq)) - #TODO !!!! Killing output as I have not tested the above code !!!! + # TODO !!!! Killing output as I have not tested the above code !!!! f_output = None return f_output + def create_auto_file(equations, params, continuation_variables): - ''' - Creates the auto configuration file and the model file. + """Creates the auto configuration file and the model file. Saves files to specified folder. Parameters ---------- - equations: Dict + equations: dict Dictionary of the substituted symbolic model equations - params: QGParams + params: QgParams The parameters fully specifying the model configuration. continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) Variables that are not substituted with numerical values. If None, no symbols are substituted - ''' + """ - #TODO: Find out best way to save these files - #TODO: There is some weird double tab spacings in the output, and I am not sure why + # TODO: Find out best way to save these files + # TODO: There is some weird double tab spacings in the output, and I am not sure why # User passes the equations, with the variables to leave as variables. # The existing model parameters are used to populate the auto file # The variables given as `continuation_variables` remain in the equations. - # There is a limit of 1-10 remian variables + # There is a limit of 1-10 remaining variables base_path = os.path.dirname(__file__) base_file = '.modelproto' base_config = '.cproto' @@ -452,7 +461,7 @@ def create_auto_file(equations, params, continuation_variables): var_list.append(temp_str) var_ini.append(initial_value) - ###### Writing model file ################ + # Writing model file ################ # Open base file and input strings f_base = open(base_path + '/' + base_file, 'r') @@ -460,9 +469,9 @@ def create_auto_file(equations, params, continuation_variables): f_base.close() auto_file = list() - #TODO: Tabs not working here correctly + # TODO: Tabs not working here correctly for ln in lines: - if 'PARAMETER DECLERATION' in ln: + if 'PARAMETER DECLARATION' in ln: for dv in declare_var: auto_file.append('\t' + dv) elif 'CONTINUATION PARAMETERS' in ln: @@ -479,7 +488,7 @@ def create_auto_file(equations, params, continuation_variables): print('\n'.join(auto_file)) - ###### Writing config file ################ + # Writing config file ################ c_base = open(base_path + '/' + base_config, 'r') lines = c_base.readlines() @@ -513,15 +522,15 @@ def create_auto_file(equations, params, continuation_variables): print('\n'.join(auto_config)) return equations - + + def _split_equations(eq_dict, f_output, line_len=80): - ''' - Function to split FORTRAN equaitons to a set length when producing functions - ''' + """Function to split FORTRAN equations to a set length when producing functions""" + for n, eq in enumerate(eq_dict.values()): - # split equaitons to be a maximum of `line_len` + # split equations to be a maximum of `line_len` - #split remainder of equation into chunkcs of length `line_length` + # split remainder of equation into chunks of length `line_length` eq_chunks = [eq[x: x + line_len] for x in range(0, len(eq), line_len)] f_output.append('\tF('+str(n+1)+') =\t ' + eq_chunks[0] + "&") for ln in eq_chunks[1:-1]: @@ -531,6 +540,7 @@ def _split_equations(eq_dict, f_output, line_len=80): f_output.append('') return f_output + def _variable_names(params): # Function to make the variable names for auto num_v = params.number_of_variables @@ -559,4 +569,4 @@ def _variable_names(params): for i, v in enumerate(var_list): output[i+1] = v - return output \ No newline at end of file + return output diff --git a/qgs/inner_products/definition.py b/qgs/inner_products/definition.py index 6dbda72..84ccce0 100644 --- a/qgs/inner_products/definition.py +++ b/qgs/inner_products/definition.py @@ -285,7 +285,7 @@ def ip_jac_lap(self, S, G, H, symbolic_expr=False, integrand=False): class StandardSymbolicInnerProductDefinition(SymbolicInnerProductDefinition): - """Standard `qgs` class to define symbolic inner products using `Sympy`_. + """Standard qgs class to define symbolic inner products using `Sympy`_. Parameters ---------- From 600772e4e654b75568a1ff9eca3baee0f62f86ad Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 9 Oct 2023 21:15:22 +0200 Subject: [PATCH 074/143] Added a symbolic expression property for the parameters Implemented the addition between parameters (WIP) --- qgs/params/parameter.py | 74 +++++++++++++++++++++++++++++++- qgs/tensors/symbolic_qgtensor.py | 4 +- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 3d5a78e..5236464 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -139,6 +139,8 @@ class Parameter(float): String describing the parameter. symbol: ~sympy.core.symbol.Symbol, optional A `Sympy`_ symbol to represent the parameter in symbolic expressions. + symbolic_expression: ~sympy.core.expr.Expr, optional + A `Sympy`_ expression to represent a relationship to other parameters. return_dimensional: bool, optional Defined if the value returned by the parameter is dimensional or not. Default to `False`. @@ -155,7 +157,7 @@ class Parameter(float): """ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, description="", - symbol=None, return_dimensional=False): + symbol=None, return_dimensional=False, symbolic_expression=None): no_scale = False @@ -191,6 +193,7 @@ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, des f._scale_object = scale_object f._description = description f._symbol = symbol + f._symbolic_expression = symbolic_expression return f @@ -215,6 +218,14 @@ def symbol(self): """~sympy.core.symbol.Symbol: Returns the symbol of the parameter.""" return self._symbol + @property + def symbolic_expression(self): + """~sympy.core.expr.Expr: Returns the symbolic expression of the parameter.""" + if self._symbolic_expression is None and self._symbol is not None: + return self._symbol + else: + return self._symbolic_expression + @property def input_dimensional(self): """bool: Indicate if the provided value is dimensional or not.""" @@ -264,6 +275,59 @@ def _nondimensionalization(self): else: return self._conversion_factor(self._units, self._scale_object) + def __add__(self, other): + + res = float(self) + float(other) + if isinstance(other, Parameter): + if self.return_dimensional != other.return_dimensional: + raise ArithmeticError("Parameter class: Impossible to add a dimensional parameter with a non-dimensional one.") + if self.units != other.units: + raise ArithmeticError("Parameter class: Impossible to add two parameters with different units.") + if self.symbolic_expression is None: + if other.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol + other.symbol + else: + expr = None + else: + if self.symbol is not None: + expr = self.symbol + other.symbolic_expression + else: + expr = None + else: + if other.symbolic_expression is None: + if other.symbol is not None: + expr = self.symbolic_expression + other.symbol + else: + expr = None + else: + expr = self.symbolic_expression + other.symbolic_expression + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=self.description + " + " + other.description, units=self.units, symbol=None, symbolic_expression=expr) + + elif isinstance(other, ScalingParameter): + if self.units != other.units: + raise ArithmeticError("Parameter class: Impossible to add two parameters with different units.") + if self.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol + other.symbol + else: + expr = None + else: + if other.symbol is not None: + expr = self.symbolic_expression + other.symbol + else: + expr = None + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=self.description + " + " + other.description, units=self.units, symbol=None, symbolic_expression=expr) + else: + return res + + def __radd__(self, other): + self.__add__(other) + class ParametersArray(np.ndarray): """Base class of model's array of parameters. @@ -368,6 +432,14 @@ def symbols(self): symbols[idx] = self[idx].symbol return symbols + @property + def symbolic_expressions(self): + """~numpy.ndarray(~sympy.core.expr.Expr): Returns the symbolic expressions of the parameters in the array.""" + symbolic_expressions = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + symbolic_expressions[idx] = self[idx].symbolic_expression + return symbolic_expressions + @property def input_dimensional(self): """bool: Indicate if the provided value is dimensional or not.""" diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index ef120c1..44dd583 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -1549,9 +1549,7 @@ def _parameter_substitutions(params, continuation_varaibles): def _parameter_values(pars): - """ - Function takes a parameter class and produces a dictionary of the symbol and the corresponding numerical value - """ + """Function takes a parameter class and produces a dictionary of the symbol and the corresponding numerical value""" subs = dict() for val in pars.__dict__.values(): From 4936a418cbd774f48cdf28f0105d927215ac9bbc Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 10 Oct 2023 11:03:25 +0200 Subject: [PATCH 075/143] Implemented basic operations for parameters preserving units and symbolic expressions --- qgs/params/parameter.py | 304 +++++++++++++++++++++++++++++++++++++++- qgs/params/params.py | 2 +- 2 files changed, 301 insertions(+), 5 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 5236464..9dd18da 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -118,6 +118,45 @@ def description(self): """str: Description of the parameter.""" return self._description + def __add__(self, other): + + if isinstance(other, Parameter): + return other.__add__(self) + else: + return float(self) + other + + def __radd__(self, other): + self.__add__(other) + + def __sub__(self, other): + + if isinstance(other, Parameter): + return other.__sub__(self) + else: + return float(self) - other + + def __rsub__(self, other): + self.__sub__(other) + + def __mul__(self, other): + + if isinstance(other, Parameter): + return other.__mul__(self) + else: + return float(self) * other + + def __rmul__(self, other): + self.__mul__(other) + + def __truediv__(self, other): + if isinstance(other, Parameter): + return other.__truediv__(self) + else: + return float(self) / other + + def __rtruediv__(self, other): + self.__truediv__(other) + class Parameter(float): """Base class of model's parameter. @@ -303,8 +342,9 @@ def __add__(self, other): else: expr = self.symbolic_expression + other.symbolic_expression - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, - description=self.description + " + " + other.description, units=self.units, symbol=None, symbolic_expression=expr) + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=self.description + " + " + other.description, + units=self.units, symbol=None, symbolic_expression=expr) elif isinstance(other, ScalingParameter): if self.units != other.units: @@ -320,14 +360,270 @@ def __add__(self, other): else: expr = None - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, - description=self.description + " + " + other.description, units=self.units, symbol=None, symbolic_expression=expr) + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=self.description + " + " + other.description, + units=self.units, symbol=None, symbolic_expression=expr) else: return res def __radd__(self, other): self.__add__(other) + def __sub__(self, other): + + res = float(self) - float(other) + if isinstance(other, Parameter): + if self.return_dimensional != other.return_dimensional: + raise ArithmeticError("Parameter class: Impossible to add a dimensional parameter with a non-dimensional one.") + if self.units != other.units: + raise ArithmeticError("Parameter class: Impossible to add two parameters with different units.") + if self.symbolic_expression is None: + if other.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol - other.symbol + else: + expr = None + else: + if self.symbol is not None: + expr = self.symbol - other.symbolic_expression + else: + expr = None + else: + if other.symbolic_expression is None: + if other.symbol is not None: + expr = self.symbolic_expression - other.symbol + else: + expr = None + else: + expr = self.symbolic_expression - other.symbolic_expression + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=self.description + " - " + other.description, + units=self.units, symbol=None, symbolic_expression=expr) + + elif isinstance(other, ScalingParameter): + if self.units != other.units: + raise ArithmeticError("Parameter class: Impossible to add two parameters with different units.") + if self.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol + other.symbol + else: + expr = None + else: + if other.symbol is not None: + expr = self.symbolic_expression + other.symbol + else: + expr = None + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=self.description + " - " + other.description, + units=self.units, symbol=None, symbolic_expression=expr) + else: + return res + + def __rsub__(self, other): + self.__sub__(other) + + def __mul__(self, other): + + res = float(self) * float(other) + if hasattr(other, "units"): + ul = self.units.split('][') + ul[0] = ul[0][1:] + ul[-1] = ul[-1][:-1] + ol = other.units.split('][') + ol[0] = ol[0][1:] + ol[-1] = ol[-1][:-1] + + usl = list() + for us in ul: + up = us.split('^') + if len(up) == 1: + up.append("1") + + usl.append(tuple(up)) + + osl = list() + for os in ol: + op = os.split('^') + if len(op) == 1: + op.append("1") + + osl.append(tuple(op)) + + units_elements = list() + for us in usl: + new_us = [us[0]] + i = 0 + for os in osl: + if os[0] == us[0]: + power = int(os[1]) + int(us[1]) + del osl[i] + break + i += 1 + else: + power = int(us[1]) + + if power == 0: + new_us = None + else: + new_us.append(str(power)) + units_elements.append(new_us) + + if osl: + units_elements += osl + + units = ["[" + us[0] + "^" + us[1] + "]" for us in units_elements if us is not None] + units = "".join(units) + else: + units = "" + + if isinstance(other, Parameter): + if self.symbolic_expression is None: + if other.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol * other.symbol + else: + expr = None + else: + if self.symbol is not None: + expr = self.symbol * other.symbolic_expression + else: + expr = None + else: + if other.symbolic_expression is None: + if other.symbol is not None: + expr = self.symbolic_expression * other.symbol + else: + expr = None + else: + expr = self.symbolic_expression * other.symbolic_expression + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=self.description + " * " + other.description, + units=units, symbol=None, symbolic_expression=expr) + + elif isinstance(other, ScalingParameter): + if self.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol * other.symbol + else: + expr = None + else: + if other.symbol is not None: + expr = self.symbolic_expression * other.symbol + else: + expr = None + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=self.description + " * " + other.description, + units=units, symbol=None, symbolic_expression=expr) + else: + return res + + def __rmul__(self, other): + self.__mul__(other) + + def __truediv__(self, other): + + res = float(self) / float(other) + if hasattr(other, "units"): + ul = self.units.split('][') + ul[0] = ul[0][1:] + ul[-1] = ul[-1][:-1] + ol = other.units.split('][') + ol[0] = ol[0][1:] + ol[-1] = ol[-1][:-1] + + usl = list() + for us in ul: + up = us.split('^') + if len(up) == 1: + up.append("1") + + usl.append(tuple(up)) + + osl = list() + for os in ol: + op = os.split('^') + if len(op) == 1: + op.append("1") + + osl.append(tuple(op)) + + units_elements = list() + for us in usl: + new_us = [us[0]] + i = 0 + for os in osl: + if os[0] == us[0]: + power = int(os[1]) - int(us[1]) + del osl[i] + break + i += 1 + else: + power = int(us[1]) + + if power == 0: + new_us = None + else: + new_us.append(str(power)) + units_elements.append(new_us) + + if osl: + units_elements += osl + + units = ["[" + us[0] + "^" + us[1] + "]" for us in units_elements if us is not None] + units = "".join(units) + else: + units = "" + + if isinstance(other, Parameter): + if self.symbolic_expression is None: + if other.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol / other.symbol + else: + expr = None + else: + if self.symbol is not None: + expr = self.symbol / other.symbolic_expression + else: + expr = None + else: + if other.symbolic_expression is None: + if other.symbol is not None: + expr = self.symbolic_expression / other.symbol + else: + expr = None + else: + expr = self.symbolic_expression / other.symbolic_expression + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=self.description + " / " + other.description, + units=units, symbol=None, symbolic_expression=expr) + + elif isinstance(other, ScalingParameter): + if self.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol / other.symbol + else: + expr = None + else: + if other.symbol is not None: + expr = self.symbolic_expression / other.symbol + else: + expr = None + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=self.description + " / " + other.description, + units=units, symbol=None, symbolic_expression=expr) + else: + return res + + def __rtruediv__(self, other): + self.__truediv__(other) + class ParametersArray(np.ndarray): """Base class of model's array of parameters. diff --git a/qgs/params/params.py b/qgs/params/params.py index d00111e..8f44b75 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -231,7 +231,7 @@ def __init__(self, dic=None): self.deltap = ScalingParameter(5.e4, units='[Pa]', description='pressure difference between the two atmospheric layers', dimensional=True) self.Ha = ScalingParameter(8500., units='[m]', description="Average height of the 500 hPa pressure level at midlatitude", - dimensional=True) + dimensional=True, symbol=Symbol('H_a')) self.set_params(dic) # ---------------------------------------- From 11a6f9385a6316d271e2e041bfbb01f007319019 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 10 Oct 2023 12:34:04 +0200 Subject: [PATCH 076/143] Finished operations definition for the Parameter class Still need to do ScalingParameter class --- qgs/params/parameter.py | 156 +++++++++++++++++++++++++++++----------- 1 file changed, 114 insertions(+), 42 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 9dd18da..e3ea90b 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -118,44 +118,76 @@ def description(self): """str: Description of the parameter.""" return self._description - def __add__(self, other): - - if isinstance(other, Parameter): - return other.__add__(self) - else: - return float(self) + other - - def __radd__(self, other): - self.__add__(other) - - def __sub__(self, other): - - if isinstance(other, Parameter): - return other.__sub__(self) - else: - return float(self) - other - - def __rsub__(self, other): - self.__sub__(other) - - def __mul__(self, other): - - if isinstance(other, Parameter): - return other.__mul__(self) - else: - return float(self) * other - - def __rmul__(self, other): - self.__mul__(other) - - def __truediv__(self, other): - if isinstance(other, Parameter): - return other.__truediv__(self) - else: - return float(self) / other - - def __rtruediv__(self, other): - self.__truediv__(other) + # def __add__(self, other): + # + # if isinstance(other, Parameter): + # return other.__add__(self) + # else: + # return float(self) + other + # + # def __radd__(self, other): + # self.__add__(other) + # + # def __sub__(self, other): + # + # if isinstance(other, Parameter): + # return other.__sub__(self) + # else: + # return float(self) - other + # + # def __rsub__(self, other): + # self.__sub__(other) + # + # def __mul__(self, other): + # + # if isinstance(other, Parameter): + # return other.__mul__(self) + # else: + # return float(self) * other + # + # def __rmul__(self, other): + # self.__mul__(other) + # + # def __truediv__(self, other): + # if isinstance(other, Parameter): + # return other.__truediv__(self) + # else: + # return float(self) / other + # + # def __rtruediv__(self, other): + # self.__truediv__(other) + # + # def __pow__(self, power, modulo=None): + # + # if modulo is not None: + # raise NotImplemented('Parameter class: Modular exponentiation not implemented') + # + # res = float(self) ** power + # if int(power) == power: + # + # ul = self.units.split('][') + # ul[0] = ul[0][1:] + # ul[-1] = ul[-1][:-1] + # + # usl = list() + # for us in ul: + # up = us.split('^') + # if len(up) == 1: + # up.append("1") + # + # usl.append(tuple(up)) + # + # units_elements = list() + # for us in usl: + # units_elements.append(list((us[0], str(int(us[1]) * power)))) + # + # units = ["[" + us[0] + "^" + us[1] + "]" for us in units_elements if us is not None] + # units = "".join(units) + # + # return Parameter(res, description=self.description + " to the power "+str(power), units=units, + # symbol=None, symbolic_expression=self.symbol ** power) + # else: + # return res class Parameter(float): @@ -553,7 +585,7 @@ def __truediv__(self, other): units_elements = list() for us in usl: - new_us = [us[0]] + new_us = list((us[0],)) i = 0 for os in osl: if os[0] == us[0]: @@ -587,17 +619,17 @@ def __truediv__(self, other): expr = None else: if self.symbol is not None: - expr = self.symbol / other.symbolic_expression + expr = self.symbol / (other.symbolic_expression) else: expr = None else: if other.symbolic_expression is None: if other.symbol is not None: - expr = self.symbolic_expression / other.symbol + expr = (self.symbolic_expression) / other.symbol else: expr = None else: - expr = self.symbolic_expression / other.symbolic_expression + expr = (self.symbolic_expression) / (other.symbolic_expression) return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=self.description + " / " + other.description, @@ -624,6 +656,46 @@ def __truediv__(self, other): def __rtruediv__(self, other): self.__truediv__(other) + def __pow__(self, power, modulo=None): + + if modulo is not None: + raise NotImplemented('Parameter class: Modular exponentiation not implemented') + + res = float(self) ** power + if int(power) == power: + + ul = self.units.split('][') + ul[0] = ul[0][1:] + ul[-1] = ul[-1][:-1] + + usl = list() + for us in ul: + up = us.split('^') + if len(up) == 1: + up.append("1") + + usl.append(tuple(up)) + + units_elements = list() + for us in usl: + units_elements.append(list((us[0], str(int(us[1]) * power)))) + + units = ["[" + us[0] + "^" + us[1] + "]" for us in units_elements if us is not None] + units = "".join(units) + + if self.symbolic_expression is not None: + expr = self.symbolic_expression ** power + elif self.symbol is not None: + expr = self.symbol ** power + else: + expr = None + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + description=self.description + " to the power "+str(power), units=units, + scale_object=self._scale_object, symbol=None, symbolic_expression=expr) + else: + return res + class ParametersArray(np.ndarray): """Base class of model's array of parameters. From 4b33c2b0256f52c9f525fd4ccf7371fb23b99e62 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 10 Oct 2023 12:35:57 +0200 Subject: [PATCH 077/143] Cont'd --- qgs/params/parameter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index e3ea90b..9544785 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -643,7 +643,7 @@ def __truediv__(self, other): expr = None else: if other.symbol is not None: - expr = self.symbolic_expression / other.symbol + expr = (self.symbolic_expression) / other.symbol else: expr = None @@ -684,7 +684,7 @@ def __pow__(self, power, modulo=None): units = "".join(units) if self.symbolic_expression is not None: - expr = self.symbolic_expression ** power + expr = (self.symbolic_expression) ** power elif self.symbol is not None: expr = self.symbol ** power else: From 2dd9859060676f438321f47e91ab884629a9ec3c Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 10 Oct 2023 16:36:15 +0200 Subject: [PATCH 078/143] Finished operations definition for the all classes --- qgs/params/parameter.py | 786 ++++++++++++++++++++++++++-------------- 1 file changed, 506 insertions(+), 280 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 9544785..7ca2d54 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -75,10 +75,12 @@ class ScalingParameter(float): Empty by default. description: str, optional String describing the parameter. - symbol: ~sympy.core.symbol.Symbol, optional - A `Sympy`_ symbol to represent the parameter in symbolic expressions. dimensional: bool, optional Indicate if the value of the parameter is dimensional or not. Default to `True`. + symbol: ~sympy.core.symbol.Symbol, optional + A `Sympy`_ symbol to represent the parameter in symbolic expressions. + symbolic_expression: ~sympy.core.expr.Expr, optional + A `Sympy`_ expression to represent a relationship to other parameters. Notes ----- @@ -88,13 +90,14 @@ class ScalingParameter(float): .. _Sympy: https://www.sympy.org/ """ - def __new__(cls, value, units="", description="", symbol=None, dimensional=False): + def __new__(cls, value, units="", description="", dimensional=False, symbol=None, symbolic_expression=None): f = float.__new__(cls, value) f._dimensional = dimensional f._units = units f._description = description f._symbol = symbol + f._symbolic_expression = symbolic_expression return f @@ -103,6 +106,14 @@ def symbol(self): """~sympy.core.symbol.Symbol: Returns the symbol of the parameter.""" return self._symbol + @property + def symbolic_expression(self): + """~sympy.core.expr.Expr: Returns the symbolic expression of the parameter.""" + if self._symbolic_expression is None and self._symbol is not None: + return self._symbol + else: + return self._symbolic_expression + @property def dimensional(self): """bool: Indicate if the returned value is dimensional or not.""" @@ -118,76 +129,286 @@ def description(self): """str: Description of the parameter.""" return self._description - # def __add__(self, other): - # - # if isinstance(other, Parameter): - # return other.__add__(self) - # else: - # return float(self) + other - # - # def __radd__(self, other): - # self.__add__(other) - # - # def __sub__(self, other): - # - # if isinstance(other, Parameter): - # return other.__sub__(self) - # else: - # return float(self) - other - # - # def __rsub__(self, other): - # self.__sub__(other) - # - # def __mul__(self, other): - # - # if isinstance(other, Parameter): - # return other.__mul__(self) - # else: - # return float(self) * other - # - # def __rmul__(self, other): - # self.__mul__(other) - # - # def __truediv__(self, other): - # if isinstance(other, Parameter): - # return other.__truediv__(self) - # else: - # return float(self) / other - # - # def __rtruediv__(self, other): - # self.__truediv__(other) - # - # def __pow__(self, power, modulo=None): - # - # if modulo is not None: - # raise NotImplemented('Parameter class: Modular exponentiation not implemented') - # - # res = float(self) ** power - # if int(power) == power: - # - # ul = self.units.split('][') - # ul[0] = ul[0][1:] - # ul[-1] = ul[-1][:-1] - # - # usl = list() - # for us in ul: - # up = us.split('^') - # if len(up) == 1: - # up.append("1") - # - # usl.append(tuple(up)) - # - # units_elements = list() - # for us in usl: - # units_elements.append(list((us[0], str(int(us[1]) * power)))) - # - # units = ["[" + us[0] + "^" + us[1] + "]" for us in units_elements if us is not None] - # units = "".join(units) - # - # return Parameter(res, description=self.description + " to the power "+str(power), units=units, - # symbol=None, symbolic_expression=self.symbol ** power) - # else: - # return res + def __add__(self, other): + + res = float(self) + float(other) + if isinstance(other, (Parameter, ScalingParameter)): + if self.units != other.units: + raise ArithmeticError("ScalingParameter class: Impossible to add two parameters with different units.") + if self.symbolic_expression is None: + if other.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol + other.symbol + else: + expr = None + descr = self.description + " + " + other.description + else: + if self.symbol is not None: + expr = self.symbol + (other.symbolic_expression) + descr = self.description + " + ( " + other.description + " )" + else: + expr = None + descr = self.description + " + " + other.description + else: + if other.symbolic_expression is None: + if other.symbol is not None: + expr = (self.symbolic_expression) + other.symbol + descr = "( " + self.description + " ) + " + other.description + else: + expr = None + descr = self.description + " + " + other.description + else: + expr = (self.symbolic_expression) + (other.symbolic_expression) + descr = "( " + self.description + " ) + ( " + other.description + " )" + + if isinstance(other, Parameter): + return Parameter(res, input_dimensional=other.input_dimensional, + return_dimensional=other.return_dimensional, scale_object=other._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) + + else: + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + else: + if self.symbol is not None: + expr = self.symbol + other + descr = self.description + " + " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) + other + descr = "( " + self.description + " ) + " + str(other) + else: + expr = None + descr = self.description + " + " + str(other) + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + + res = float(self) - float(other) + if isinstance(other, (Parameter, ScalingParameter)): + if self.units != other.units: + raise ArithmeticError("ScalingParameter class: Impossible to subtract two parameters with different units.") + if self.symbolic_expression is None: + if other.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol - other.symbol + else: + expr = None + descr = self.description + " - " + other.description + else: + if self.symbol is not None: + expr = self.symbol - (other.symbolic_expression) + descr = self.description + " - ( " + other.description + " )" + else: + expr = None + descr = self.description + " - " + other.description + else: + if other.symbolic_expression is None: + if other.symbol is not None: + expr = (self.symbolic_expression) - other.symbol + descr = "( " + self.description + " ) - " + other.description + else: + expr = None + descr = self.description + " - " + other.description + else: + expr = (self.symbolic_expression) - (other.symbolic_expression) + descr = "( " + self.description + " ) - ( " + other.description + " )" + + if isinstance(other, Parameter): + return Parameter(res, input_dimensional=other.input_dimensional, + return_dimensional=other.return_dimensional, scale_object=other._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) + + else: + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + else: + if self.symbol is not None: + expr = self.symbol - other + descr = self.description + " - " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) - other + descr = "( " + self.description + " ) - " + str(other) + else: + expr = None + descr = self.description + " - " + str(other) + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + + def __rsub__(self, other): + res = float(other) - float(self) + if self.symbol is not None: + expr = other - self.symbol + descr = str(other) + " - " + self.description + elif self.symbolic_expression is not None: + expr = other - (self.symbolic_expression) + descr = str(other) + " - ( " + self.description + " )" + else: + expr = None + descr = str(other) + " - " + self.description + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + + def __mul__(self, other): + + res = float(self) * float(other) + if isinstance(other, (Parameter, ScalingParameter)): + units = _join_units(self.units, other.units, '+') + + if self.symbolic_expression is None: + if other.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol * other.symbol + else: + expr = None + descr = self.description + " * " + other.description + else: + if self.symbol is not None: + expr = self.symbol * (other.symbolic_expression) + descr = self.description + " * ( " + other.description + " )" + else: + expr = None + descr = self.description + " * " + other.description + else: + if other.symbolic_expression is None: + if other.symbol is not None: + expr = (self.symbolic_expression) * other.symbol + descr = "( " + self.description + " ) * " + other.description + else: + expr = None + descr = self.description + " * " + other.description + else: + expr = (self.symbolic_expression) * (other.symbolic_expression) + descr = "( " + self.description + " ) * ( " + other.description + " )" + + if isinstance(other, Parameter): + return Parameter(res, input_dimensional=other.input_dimensional, return_dimensional=other.return_dimensional, + scale_object=other._scale_object, description=descr, + units=units, symbol=None, symbolic_expression=expr) + + else: + return ScalingParameter(res, description=descr, units=units, symbol=None, symbolic_expression=expr) + else: + if self.symbol is not None: + expr = self.symbol * other + descr = self.description + " * " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) * other + descr = "( " + self.description + " ) * " + str(other) + else: + expr = None + descr = self.description + " * " + str(other) + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + + def __rmul__(self, other): + return self.__mul__(other) + + def __truediv__(self, other): + + res = float(self) / float(other) + if isinstance(other, (ScalingParameter, Parameter)): + units = _join_units(self.units, other.units, '-') + if self.symbolic_expression is None: + if other.symbolic_expression is None: + if self.symbol is not None and other.symbol is not None: + expr = self.symbol / other.symbol + else: + expr = None + descr = self.description + " / " + other.description + else: + if self.symbol is not None: + expr = self.symbol / (other.symbolic_expression) + descr = self.description + " / ( " + other.description + " )" + else: + expr = None + descr = self.description + " / " + other.description + else: + if other.symbolic_expression is None: + if other.symbol is not None: + expr = (self.symbolic_expression) / other.symbol + descr = "( " + self.description + " ) / " + other.description + else: + expr = None + descr = self.description + " / " + other.description + else: + expr = (self.symbolic_expression) / (other.symbolic_expression) + descr = "( " + self.description + " ) / ( " + other.description + " )" + + if isinstance(other, Parameter): + return Parameter(res, input_dimensional=other.input_dimensional, return_dimensional=other.return_dimensional, + scale_object=other._scale_object, description=descr, + units=units, symbol=None, symbolic_expression=expr) + else: + return ScalingParameter(res, description=descr, units=units, symbol=None, symbolic_expression=expr) + else: + if self.symbol is not None: + expr = self.symbol / other + descr = self.description + " / " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) / other + descr = "( " + self.description + " ) / " + str(other) + else: + expr = None + descr = self.description + " / " + str(other) + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + + def __rtruediv__(self, other): + res = float(other) / float(self) + if self.symbol is not None: + expr = other / self.symbol + descr = str(other) + " / " + self.description + elif self.symbolic_expression is not None: + expr = other / (self.symbolic_expression) + descr = str(other) + " / ( " + self.description + " )" + else: + expr = None + descr = str(other) + " / " + self.description + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + + def __pow__(self, power, modulo=None): + + if modulo is not None: + raise NotImplemented('ScalingParameter class: Modular exponentiation not implemented') + + res = float(self) ** power + if int(power) == power: + + ul = self.units.split('][') + ul[0] = ul[0][1:] + ul[-1] = ul[-1][:-1] + + usl = list() + for us in ul: + up = us.split('^') + if len(up) == 1: + up.append("1") + + usl.append(tuple(up)) + + units_elements = list() + for us in usl: + units_elements.append(list((us[0], str(int(us[1]) * power)))) + + units = list() + for us in units_elements: + if us is not None: + if int(us[1]) != 1: + units.append("[" + us[0] + "^" + us[1] + "]") + else: + units.append("[" + us[0] + "]") + units = "".join(units) + + if self.symbolic_expression is not None: + expr = (self.symbolic_expression) ** power + descr = "( " + self.description + " ) to the power "+str(power) + elif self.symbol is not None: + expr = self.symbol ** power + descr = self.description + " to the power "+str(power) + else: + expr = None + descr = self.description + " to the power "+str(power) + + return ScalingParameter(res, description=descr, units=units, symbol=None, symbolic_expression=expr) + else: + return res class Parameter(float): @@ -249,7 +470,14 @@ def __new__(cls, value, input_dimensional=True, units="", scale_object=None, des evalue = value no_scale = True else: - evalue = value * cls._conversion_factor(units, scale_object) + try: + evalue = value * cls._conversion_factor(units, scale_object) + except: + print(description) + print(symbol) + print(units) + print(cls._conversion_factor(units, scale_object)) + print(scale_object) else: evalue = value @@ -349,312 +577,241 @@ def _nondimensionalization(self): def __add__(self, other): res = float(self) + float(other) - if isinstance(other, Parameter): - if self.return_dimensional != other.return_dimensional: - raise ArithmeticError("Parameter class: Impossible to add a dimensional parameter with a non-dimensional one.") + if isinstance(other, (Parameter, ScalingParameter)): + if isinstance(other, Parameter) and self.return_dimensional != other.return_dimensional: + raise ArithmeticError("Parameter class: Impossible to subtract a dimensional parameter with a non-dimensional one.") if self.units != other.units: raise ArithmeticError("Parameter class: Impossible to add two parameters with different units.") + if self.symbolic_expression is None: if other.symbolic_expression is None: if self.symbol is not None and other.symbol is not None: expr = self.symbol + other.symbol else: expr = None + descr = self.description + " + " + other.description else: if self.symbol is not None: - expr = self.symbol + other.symbolic_expression + expr = self.symbol + (other.symbolic_expression) + descr = self.description + " + ( " + other.description + " )" else: expr = None + descr = self.description + " + " + other.description else: if other.symbolic_expression is None: if other.symbol is not None: - expr = self.symbolic_expression + other.symbol + expr = (self.symbolic_expression) + other.symbol + descr = "( " + self.description + " ) + " + other.description else: expr = None + descr = self.description + " + " + other.description else: - expr = self.symbolic_expression + other.symbolic_expression - - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=self.description + " + " + other.description, - units=self.units, symbol=None, symbolic_expression=expr) - - elif isinstance(other, ScalingParameter): - if self.units != other.units: - raise ArithmeticError("Parameter class: Impossible to add two parameters with different units.") - if self.symbolic_expression is None: - if self.symbol is not None and other.symbol is not None: - expr = self.symbol + other.symbol - else: - expr = None - else: - if other.symbol is not None: - expr = self.symbolic_expression + other.symbol - else: - expr = None + expr = (self.symbolic_expression) + (other.symbolic_expression) + descr = "( " + self.description + " ) + ( " + other.description + " )" - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=self.description + " + " + other.description, - units=self.units, symbol=None, symbolic_expression=expr) + return Parameter(res, input_dimensional=self.input_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) else: - return res + if self.symbol is not None: + expr = self.symbol + other + descr = self.description + " + " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) + other + descr = "( " + self.description + " ) + " + str(other) + else: + expr = None + descr = self.description + " + " + str(other) + return Parameter(res, input_dimensional=self.input_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) def __radd__(self, other): - self.__add__(other) + return self.__add__(other) def __sub__(self, other): res = float(self) - float(other) - if isinstance(other, Parameter): - if self.return_dimensional != other.return_dimensional: - raise ArithmeticError("Parameter class: Impossible to add a dimensional parameter with a non-dimensional one.") + if isinstance(other, (Parameter, ScalingParameter)): + if isinstance(other, Parameter) and self.return_dimensional != other.return_dimensional: + raise ArithmeticError("Parameter class: Impossible to subtract a dimensional parameter with a non-dimensional one.") if self.units != other.units: - raise ArithmeticError("Parameter class: Impossible to add two parameters with different units.") + raise ArithmeticError("Parameter class: Impossible to subtract two parameters with different units.") if self.symbolic_expression is None: if other.symbolic_expression is None: if self.symbol is not None and other.symbol is not None: expr = self.symbol - other.symbol else: expr = None + descr = self.description + " - " + other.description else: if self.symbol is not None: - expr = self.symbol - other.symbolic_expression + expr = self.symbol - (other.symbolic_expression) + descr = self.description + " - ( " + other.description + " )" else: expr = None + descr = self.description + " - " + other.description else: if other.symbolic_expression is None: if other.symbol is not None: - expr = self.symbolic_expression - other.symbol + expr = (self.symbolic_expression) - other.symbol + descr = "( " + self.description + " ) - " + other.description else: expr = None + descr = self.description + " - " + other.description else: - expr = self.symbolic_expression - other.symbolic_expression - - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=self.description + " - " + other.description, - units=self.units, symbol=None, symbolic_expression=expr) - - elif isinstance(other, ScalingParameter): - if self.units != other.units: - raise ArithmeticError("Parameter class: Impossible to add two parameters with different units.") - if self.symbolic_expression is None: - if self.symbol is not None and other.symbol is not None: - expr = self.symbol + other.symbol - else: - expr = None - else: - if other.symbol is not None: - expr = self.symbolic_expression + other.symbol - else: - expr = None + expr = (self.symbolic_expression) - (other.symbolic_expression) + descr = "( " + self.description + " ) - ( " + other.description + " )" - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=self.description + " - " + other.description, - units=self.units, symbol=None, symbolic_expression=expr) + return Parameter(res, input_dimensional=self.input_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) else: - return res + if self.symbol is not None: + expr = self.symbol - other + descr = self.description + " - " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) - other + descr = "( " + self.description + " ) - " + str(other) + else: + expr = None + descr = self.description + " - " + str(other) + return Parameter(res, input_dimensional=self.input_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) def __rsub__(self, other): - self.__sub__(other) + res = float(other) - float(self) + if self.symbol is not None: + expr = other - self.symbol + descr = str(other) + " - " + self.description + elif self.symbolic_expression is not None: + expr = other - (self.symbolic_expression) + descr = str(other) + " - ( " + self.description + " )" + else: + expr = None + descr = str(other) + " - " + self.description + return Parameter(res, input_dimensional=self.input_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) def __mul__(self, other): res = float(self) * float(other) - if hasattr(other, "units"): - ul = self.units.split('][') - ul[0] = ul[0][1:] - ul[-1] = ul[-1][:-1] - ol = other.units.split('][') - ol[0] = ol[0][1:] - ol[-1] = ol[-1][:-1] - - usl = list() - for us in ul: - up = us.split('^') - if len(up) == 1: - up.append("1") - - usl.append(tuple(up)) - - osl = list() - for os in ol: - op = os.split('^') - if len(op) == 1: - op.append("1") - - osl.append(tuple(op)) - - units_elements = list() - for us in usl: - new_us = [us[0]] - i = 0 - for os in osl: - if os[0] == us[0]: - power = int(os[1]) + int(us[1]) - del osl[i] - break - i += 1 - else: - power = int(us[1]) - - if power == 0: - new_us = None - else: - new_us.append(str(power)) - units_elements.append(new_us) - - if osl: - units_elements += osl - - units = ["[" + us[0] + "^" + us[1] + "]" for us in units_elements if us is not None] - units = "".join(units) - else: - units = "" + if isinstance(other, (Parameter, ScalingParameter)): + if hasattr(other, "units"): + units = _join_units(self.units, other.units, '+') + else: + units = "" - if isinstance(other, Parameter): if self.symbolic_expression is None: if other.symbolic_expression is None: if self.symbol is not None and other.symbol is not None: expr = self.symbol * other.symbol else: expr = None + descr = self.description + " * " + other.description else: if self.symbol is not None: - expr = self.symbol * other.symbolic_expression + expr = self.symbol * (other.symbolic_expression) + descr = self.description + " * ( " + other.description + " )" else: expr = None + descr = self.description + " * " + other.description else: if other.symbolic_expression is None: if other.symbol is not None: - expr = self.symbolic_expression * other.symbol + expr = (self.symbolic_expression) * other.symbol + descr = "( " + self.description + " ) * " + other.description else: expr = None + descr = self.description + " * " + other.description else: - expr = self.symbolic_expression * other.symbolic_expression + expr = (self.symbolic_expression) * (other.symbolic_expression) + descr = "( " + self.description + " ) * ( " + other.description + " )" return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=self.description + " * " + other.description, - units=units, symbol=None, symbolic_expression=expr) - - elif isinstance(other, ScalingParameter): - if self.symbolic_expression is None: - if self.symbol is not None and other.symbol is not None: - expr = self.symbol * other.symbol - else: - expr = None + scale_object=self._scale_object, description=descr, units=units, symbol=None, + symbolic_expression=expr) + else: + if self.symbol is not None: + expr = self.symbol * other + descr = self.description + " * " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) * other + descr = "( " + self.description + " ) * " + str(other) else: - if other.symbol is not None: - expr = self.symbolic_expression * other.symbol - else: - expr = None - + expr = None + descr = self.description + " * " + str(other) return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=self.description + " * " + other.description, - units=units, symbol=None, symbolic_expression=expr) - else: - return res + scale_object=self._scale_object, description=descr, units=self.units, symbol=None, + symbolic_expression=expr) def __rmul__(self, other): - self.__mul__(other) + return self.__mul__(other) def __truediv__(self, other): res = float(self) / float(other) - if hasattr(other, "units"): - ul = self.units.split('][') - ul[0] = ul[0][1:] - ul[-1] = ul[-1][:-1] - ol = other.units.split('][') - ol[0] = ol[0][1:] - ol[-1] = ol[-1][:-1] - - usl = list() - for us in ul: - up = us.split('^') - if len(up) == 1: - up.append("1") - - usl.append(tuple(up)) - - osl = list() - for os in ol: - op = os.split('^') - if len(op) == 1: - op.append("1") - - osl.append(tuple(op)) - - units_elements = list() - for us in usl: - new_us = list((us[0],)) - i = 0 - for os in osl: - if os[0] == us[0]: - power = int(os[1]) - int(us[1]) - del osl[i] - break - i += 1 - else: - power = int(us[1]) - - if power == 0: - new_us = None - else: - new_us.append(str(power)) - units_elements.append(new_us) - - if osl: - units_elements += osl - - units = ["[" + us[0] + "^" + us[1] + "]" for us in units_elements if us is not None] - units = "".join(units) - else: - units = "" - - if isinstance(other, Parameter): + if isinstance(other, (ScalingParameter, Parameter)): + units = _join_units(self.units, other.units, '-') if self.symbolic_expression is None: if other.symbolic_expression is None: if self.symbol is not None and other.symbol is not None: expr = self.symbol / other.symbol else: expr = None + descr = self.description + " / " + other.description else: if self.symbol is not None: expr = self.symbol / (other.symbolic_expression) + descr = self.description + " / ( " + other.description + " )" else: expr = None + descr = self.description + " / " + other.description else: if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) / other.symbol + descr = "( " + self.description + " ) / " + other.description else: expr = None + descr = self.description + " / " + other.description else: expr = (self.symbolic_expression) / (other.symbolic_expression) + descr = "( " + self.description + " ) / ( " + other.description + " )" return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=self.description + " / " + other.description, - units=units, symbol=None, symbolic_expression=expr) - - elif isinstance(other, ScalingParameter): - if self.symbolic_expression is None: - if self.symbol is not None and other.symbol is not None: - expr = self.symbol / other.symbol - else: - expr = None + scale_object=self._scale_object, description=descr, units=units, symbol=None, + symbolic_expression=expr) + else: + if self.symbol is not None: + expr = self.symbol / other + descr = self.description + " / " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) / other + descr = "( " + self.description + " ) / " + str(other) else: - if other.symbol is not None: - expr = (self.symbolic_expression) / other.symbol - else: - expr = None - + expr = None + descr = self.description + " / " + str(other) return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=self.description + " / " + other.description, - units=units, symbol=None, symbolic_expression=expr) - else: - return res + scale_object=self._scale_object, description=descr, units=self.units, symbol=None, + symbolic_expression=expr) def __rtruediv__(self, other): - self.__truediv__(other) + res = float(other) / float(self) + if self.symbol is not None: + expr = other / self.symbol + descr = str(other) + " / " + self.description + elif self.symbolic_expression is not None: + expr = other / (self.symbolic_expression) + descr = str(other) + " / ( " + self.description + " )" + else: + expr = None + descr = str(other) + " / " + self.description + return Parameter(res, input_dimensional=self.input_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) def __pow__(self, power, modulo=None): @@ -680,19 +837,28 @@ def __pow__(self, power, modulo=None): for us in usl: units_elements.append(list((us[0], str(int(us[1]) * power)))) - units = ["[" + us[0] + "^" + us[1] + "]" for us in units_elements if us is not None] + units = list() + for us in units_elements: + if us is not None: + if int(us[1]) != 1: + units.append("[" + us[0] + "^" + us[1] + "]") + else: + units.append("[" + us[0] + "]") units = "".join(units) if self.symbolic_expression is not None: expr = (self.symbolic_expression) ** power + descr = "( " + self.description + " ) to the power "+str(power) elif self.symbol is not None: expr = self.symbol ** power + descr = self.description + " to the power "+str(power) else: expr = None + descr = self.description + " to the power "+str(power) return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - description=self.description + " to the power "+str(power), units=units, - scale_object=self._scale_object, symbol=None, symbolic_expression=expr) + description=descr, units=units, scale_object=self._scale_object, symbol=None, + symbolic_expression=expr) else: return res @@ -859,3 +1025,63 @@ def _nondimensionalization(self): return 1. else: return self._conversion_factor(self._units, self._scale_object) + +def _join_units(units1, units2, operation): + ul = units1.split('][') + + + ul[0] = ul[0][1:] + ul[-1] = ul[-1][:-1] + ol = units2.split('][') + ol[0] = ol[0][1:] + ol[-1] = ol[-1][:-1] + + usl = list() + for us in ul: + up = us.split('^') + if len(up) == 1: + up.append("1") + + usl.append(tuple(up)) + + osl = list() + for os in ol: + op = os.split('^') + if len(op) == 1: + op.append("1") + + osl.append(tuple(op)) + + units_elements = list() + for us in usl: + new_us = [us[0]] + i = 0 + for os in osl: + if os[0] == us[0]: + if operation == '-': + power = int(os[1]) - int(us[1]) + else: + power = int(os[1]) + int(us[1]) + del osl[i] + break + i += 1 + else: + power = int(us[1]) + + if power == 0: + new_us = None + else: + new_us.append(str(power)) + units_elements.append(new_us) + + if osl: + units_elements += osl + + units = list() + for us in units_elements: + if us is not None: + if int(us[1]) != 1: + units.append("[" + us[0] + "^" + us[1] + "]") + else: + units.append("[" + us[0] + "]") + return "".join(units) From f5cbf373b58eb61eb6fde137518ae46105e53887 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 10 Oct 2023 17:04:17 +0200 Subject: [PATCH 079/143] Solved some bugs Still need to implement sqrt --- qgs/params/parameter.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 7ca2d54..f785969 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -1026,10 +1026,9 @@ def _nondimensionalization(self): else: return self._conversion_factor(self._units, self._scale_object) + def _join_units(units1, units2, operation): ul = units1.split('][') - - ul[0] = ul[0][1:] ul[-1] = ul[-1][:-1] ol = units2.split('][') @@ -1042,7 +1041,8 @@ def _join_units(units1, units2, operation): if len(up) == 1: up.append("1") - usl.append(tuple(up)) + if up[0]: + usl.append(tuple(up)) osl = list() for os in ol: @@ -1050,7 +1050,8 @@ def _join_units(units1, units2, operation): if len(op) == 1: op.append("1") - osl.append(tuple(op)) + if op[0]: + osl.append(tuple(op)) units_elements = list() for us in usl: @@ -1068,13 +1069,11 @@ def _join_units(units1, units2, operation): else: power = int(us[1]) - if power == 0: - new_us = None - else: + if power != 0: new_us.append(str(power)) - units_elements.append(new_us) + units_elements.append(new_us) - if osl: + if len(osl) != 0: units_elements += osl units = list() From fd16c09a57cb1c4e6732c2aa5e11202338c67e3b Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 10 Oct 2023 17:57:17 +0200 Subject: [PATCH 080/143] Implemented sqrt for parameters arithmetic --- qgs/params/parameter.py | 91 ++++++++++++++++++++++++++++++++++++++--- qgs/params/params.py | 2 +- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index f785969..4b25471 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -60,8 +60,11 @@ import warnings import numpy as np +from fractions import Fraction +# TODO: Automatize warnings and errors + class ScalingParameter(float): """Class of model's dimension parameter. @@ -406,9 +409,47 @@ def __pow__(self, power, modulo=None): expr = None descr = self.description + " to the power "+str(power) - return ScalingParameter(res, description=descr, units=units, symbol=None, symbolic_expression=expr) else: - return res + power_fraction = Fraction(power) + ul = self.units.split('][') + ul[0] = ul[0][1:] + ul[-1] = ul[-1][:-1] + + usl = list() + for us in ul: + up = us.split('^') + if len(up) == 1: + up.append("1") + + usl.append(tuple(up)) + + units_elements = list() + for us in usl: + new_power = int(us[1]) * power_fraction.numerator / power_fraction.denominator + if int(new_power) == new_power: + units_elements.append(list((us[0], str(new_power)))) + else: + raise ArithmeticError("ScalingParameter class: Only support integer exponent in units") + + units = list() + for us in units_elements: + if us is not None: + if int(us[1]) != 1: + units.append("[" + us[0] + "^" + us[1] + "]") + else: + units.append("[" + us[0] + "]") + units = "".join(units) + if self.symbolic_expression is not None: + expr = (self.symbolic_expression) ** power + descr = "( " + self.description + " ) to the power " + str(power) + elif self.symbol is not None: + expr = self.symbol ** power + descr = self.description + " to the power " + str(power) + else: + expr = None + descr = self.description + " to the power " + str(power) + + return ScalingParameter(res, description=descr, units=units, symbol=None, symbolic_expression=expr) class Parameter(float): @@ -856,11 +897,49 @@ def __pow__(self, power, modulo=None): expr = None descr = self.description + " to the power "+str(power) - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - description=descr, units=units, scale_object=self._scale_object, symbol=None, - symbolic_expression=expr) else: - return res + power_fraction = Fraction(power) + ul = self.units.split('][') + ul[0] = ul[0][1:] + ul[-1] = ul[-1][:-1] + + usl = list() + for us in ul: + up = us.split('^') + if len(up) == 1: + up.append("1") + + usl.append(tuple(up)) + + units_elements = list() + for us in usl: + new_power = int(us[1]) * power_fraction.numerator / power_fraction.denominator + if int(new_power) == new_power: + units_elements.append(list((us[0], str(int(new_power))))) + else: + raise ArithmeticError("Parameter class: Only support integer exponent in units") + + units = list() + for us in units_elements: + if us is not None: + if int(us[1]) != 1: + units.append("[" + us[0] + "^" + us[1] + "]") + else: + units.append("[" + us[0] + "]") + units = "".join(units) + if self.symbolic_expression is not None: + expr = (self.symbolic_expression) ** power + descr = "( " + self.description + " ) to the power "+str(power) + elif self.symbol is not None: + expr = self.symbol ** power + descr = self.description + " to the power "+str(power) + else: + expr = None + descr = self.description + " to the power "+str(power) + + return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + description=descr, units=units, scale_object=self._scale_object, symbol=None, + symbolic_expression=expr) class ParametersArray(np.ndarray): diff --git a/qgs/params/params.py b/qgs/params/params.py index 8f44b75..5d7cd27 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -937,7 +937,7 @@ def LR(self): scp = self.scale_params if op is not None: try: - return np.sqrt(op.gp * op.h) / scp.f0 + return (op.gp * op.h) ** 0.5 / scp.f0 except: return None else: From 685365d37d2f5f3ef8111ed6878accb5b9c2bfcc Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 10 Oct 2023 18:08:05 +0200 Subject: [PATCH 081/143] Comments --- qgs/params/parameter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 4b25471..6cd44cc 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -64,6 +64,7 @@ # TODO: Automatize warnings and errors +# TODO: Implement operations for arrays class ScalingParameter(float): """Class of model's dimension parameter. From 3c083626c0ab4e5438e2813a837ff128816b3772 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 11 Oct 2023 10:10:15 +0200 Subject: [PATCH 082/143] Updated description strings --- qgs/params/parameter.py | 80 ++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 6cd44cc..0b9025d 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -149,7 +149,7 @@ def __add__(self, other): else: if self.symbol is not None: expr = self.symbol + (other.symbolic_expression) - descr = self.description + " + ( " + other.description + " )" + descr = self.description + " + (" + other.description + ")" else: expr = None descr = self.description + " + " + other.description @@ -157,13 +157,13 @@ def __add__(self, other): if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) + other.symbol - descr = "( " + self.description + " ) + " + other.description + descr = "(" + self.description + ") + " + other.description else: expr = None descr = self.description + " + " + other.description else: expr = (self.symbolic_expression) + (other.symbolic_expression) - descr = "( " + self.description + " ) + ( " + other.description + " )" + descr = "(" + self.description + ") + (" + other.description + ")" if isinstance(other, Parameter): return Parameter(res, input_dimensional=other.input_dimensional, @@ -178,7 +178,7 @@ def __add__(self, other): descr = self.description + " + " + str(other) elif self.symbolic_expression is not None: expr = (self.symbolic_expression) + other - descr = "( " + self.description + " ) + " + str(other) + descr = "(" + self.description + ") + " + str(other) else: expr = None descr = self.description + " + " + str(other) @@ -203,7 +203,7 @@ def __sub__(self, other): else: if self.symbol is not None: expr = self.symbol - (other.symbolic_expression) - descr = self.description + " - ( " + other.description + " )" + descr = self.description + " - (" + other.description + ")" else: expr = None descr = self.description + " - " + other.description @@ -211,13 +211,13 @@ def __sub__(self, other): if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) - other.symbol - descr = "( " + self.description + " ) - " + other.description + descr = "(" + self.description + ") - " + other.description else: expr = None descr = self.description + " - " + other.description else: expr = (self.symbolic_expression) - (other.symbolic_expression) - descr = "( " + self.description + " ) - ( " + other.description + " )" + descr = "(" + self.description + ") - (" + other.description + ")" if isinstance(other, Parameter): return Parameter(res, input_dimensional=other.input_dimensional, @@ -232,7 +232,7 @@ def __sub__(self, other): descr = self.description + " - " + str(other) elif self.symbolic_expression is not None: expr = (self.symbolic_expression) - other - descr = "( " + self.description + " ) - " + str(other) + descr = "(" + self.description + ") - " + str(other) else: expr = None descr = self.description + " - " + str(other) @@ -245,7 +245,7 @@ def __rsub__(self, other): descr = str(other) + " - " + self.description elif self.symbolic_expression is not None: expr = other - (self.symbolic_expression) - descr = str(other) + " - ( " + self.description + " )" + descr = str(other) + " - (" + self.description + ")" else: expr = None descr = str(other) + " - " + self.description @@ -267,7 +267,7 @@ def __mul__(self, other): else: if self.symbol is not None: expr = self.symbol * (other.symbolic_expression) - descr = self.description + " * ( " + other.description + " )" + descr = self.description + " * (" + other.description + ")" else: expr = None descr = self.description + " * " + other.description @@ -275,13 +275,13 @@ def __mul__(self, other): if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) * other.symbol - descr = "( " + self.description + " ) * " + other.description + descr = "(" + self.description + ") * " + other.description else: expr = None descr = self.description + " * " + other.description else: expr = (self.symbolic_expression) * (other.symbolic_expression) - descr = "( " + self.description + " ) * ( " + other.description + " )" + descr = "(" + self.description + ") * (" + other.description + ")" if isinstance(other, Parameter): return Parameter(res, input_dimensional=other.input_dimensional, return_dimensional=other.return_dimensional, @@ -296,7 +296,7 @@ def __mul__(self, other): descr = self.description + " * " + str(other) elif self.symbolic_expression is not None: expr = (self.symbolic_expression) * other - descr = "( " + self.description + " ) * " + str(other) + descr = "(" + self.description + ") * " + str(other) else: expr = None descr = self.description + " * " + str(other) @@ -320,7 +320,7 @@ def __truediv__(self, other): else: if self.symbol is not None: expr = self.symbol / (other.symbolic_expression) - descr = self.description + " / ( " + other.description + " )" + descr = self.description + " / (" + other.description + ")" else: expr = None descr = self.description + " / " + other.description @@ -328,13 +328,13 @@ def __truediv__(self, other): if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) / other.symbol - descr = "( " + self.description + " ) / " + other.description + descr = "(" + self.description + ") / " + other.description else: expr = None descr = self.description + " / " + other.description else: expr = (self.symbolic_expression) / (other.symbolic_expression) - descr = "( " + self.description + " ) / ( " + other.description + " )" + descr = "(" + self.description + ") / (" + other.description + ")" if isinstance(other, Parameter): return Parameter(res, input_dimensional=other.input_dimensional, return_dimensional=other.return_dimensional, @@ -348,7 +348,7 @@ def __truediv__(self, other): descr = self.description + " / " + str(other) elif self.symbolic_expression is not None: expr = (self.symbolic_expression) / other - descr = "( " + self.description + " ) / " + str(other) + descr = "(" + self.description + ") / " + str(other) else: expr = None descr = self.description + " / " + str(other) @@ -361,7 +361,7 @@ def __rtruediv__(self, other): descr = str(other) + " / " + self.description elif self.symbolic_expression is not None: expr = other / (self.symbolic_expression) - descr = str(other) + " / ( " + self.description + " )" + descr = str(other) + " / (" + self.description + ")" else: expr = None descr = str(other) + " / " + self.description @@ -402,7 +402,7 @@ def __pow__(self, power, modulo=None): if self.symbolic_expression is not None: expr = (self.symbolic_expression) ** power - descr = "( " + self.description + " ) to the power "+str(power) + descr = "(" + self.description + ") to the power "+str(power) elif self.symbol is not None: expr = self.symbol ** power descr = self.description + " to the power "+str(power) @@ -442,7 +442,7 @@ def __pow__(self, power, modulo=None): units = "".join(units) if self.symbolic_expression is not None: expr = (self.symbolic_expression) ** power - descr = "( " + self.description + " ) to the power " + str(power) + descr = "(" + self.description + ") to the power " + str(power) elif self.symbol is not None: expr = self.symbol ** power descr = self.description + " to the power " + str(power) @@ -635,7 +635,7 @@ def __add__(self, other): else: if self.symbol is not None: expr = self.symbol + (other.symbolic_expression) - descr = self.description + " + ( " + other.description + " )" + descr = self.description + " + (" + other.description + ")" else: expr = None descr = self.description + " + " + other.description @@ -643,13 +643,13 @@ def __add__(self, other): if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) + other.symbol - descr = "( " + self.description + " ) + " + other.description + descr = "(" + self.description + ") + " + other.description else: expr = None descr = self.description + " + " + other.description else: expr = (self.symbolic_expression) + (other.symbolic_expression) - descr = "( " + self.description + " ) + ( " + other.description + " )" + descr = "(" + self.description + ") + (" + other.description + ")" return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, @@ -660,7 +660,7 @@ def __add__(self, other): descr = self.description + " + " + str(other) elif self.symbolic_expression is not None: expr = (self.symbolic_expression) + other - descr = "( " + self.description + " ) + " + str(other) + descr = "(" + self.description + ") + " + str(other) else: expr = None descr = self.description + " + " + str(other) @@ -689,7 +689,7 @@ def __sub__(self, other): else: if self.symbol is not None: expr = self.symbol - (other.symbolic_expression) - descr = self.description + " - ( " + other.description + " )" + descr = self.description + " - (" + other.description + ")" else: expr = None descr = self.description + " - " + other.description @@ -697,13 +697,13 @@ def __sub__(self, other): if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) - other.symbol - descr = "( " + self.description + " ) - " + other.description + descr = "(" + self.description + ") - " + other.description else: expr = None descr = self.description + " - " + other.description else: expr = (self.symbolic_expression) - (other.symbolic_expression) - descr = "( " + self.description + " ) - ( " + other.description + " )" + descr = "(" + self.description + ") - (" + other.description + ")" return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, @@ -714,7 +714,7 @@ def __sub__(self, other): descr = self.description + " - " + str(other) elif self.symbolic_expression is not None: expr = (self.symbolic_expression) - other - descr = "( " + self.description + " ) - " + str(other) + descr = "(" + self.description + ") - " + str(other) else: expr = None descr = self.description + " - " + str(other) @@ -729,7 +729,7 @@ def __rsub__(self, other): descr = str(other) + " - " + self.description elif self.symbolic_expression is not None: expr = other - (self.symbolic_expression) - descr = str(other) + " - ( " + self.description + " )" + descr = str(other) + " - (" + self.description + ")" else: expr = None descr = str(other) + " - " + self.description @@ -756,7 +756,7 @@ def __mul__(self, other): else: if self.symbol is not None: expr = self.symbol * (other.symbolic_expression) - descr = self.description + " * ( " + other.description + " )" + descr = self.description + " * (" + other.description + ")" else: expr = None descr = self.description + " * " + other.description @@ -764,13 +764,13 @@ def __mul__(self, other): if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) * other.symbol - descr = "( " + self.description + " ) * " + other.description + descr = "(" + self.description + ") * " + other.description else: expr = None descr = self.description + " * " + other.description else: expr = (self.symbolic_expression) * (other.symbolic_expression) - descr = "( " + self.description + " ) * ( " + other.description + " )" + descr = "(" + self.description + ") * (" + other.description + ")" return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=units, symbol=None, @@ -781,7 +781,7 @@ def __mul__(self, other): descr = self.description + " * " + str(other) elif self.symbolic_expression is not None: expr = (self.symbolic_expression) * other - descr = "( " + self.description + " ) * " + str(other) + descr = "(" + self.description + ") * " + str(other) else: expr = None descr = self.description + " * " + str(other) @@ -807,7 +807,7 @@ def __truediv__(self, other): else: if self.symbol is not None: expr = self.symbol / (other.symbolic_expression) - descr = self.description + " / ( " + other.description + " )" + descr = self.description + " / (" + other.description + ")" else: expr = None descr = self.description + " / " + other.description @@ -815,13 +815,13 @@ def __truediv__(self, other): if other.symbolic_expression is None: if other.symbol is not None: expr = (self.symbolic_expression) / other.symbol - descr = "( " + self.description + " ) / " + other.description + descr = "(" + self.description + ") / " + other.description else: expr = None descr = self.description + " / " + other.description else: expr = (self.symbolic_expression) / (other.symbolic_expression) - descr = "( " + self.description + " ) / ( " + other.description + " )" + descr = "(" + self.description + ") / (" + other.description + ")" return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=units, symbol=None, @@ -832,7 +832,7 @@ def __truediv__(self, other): descr = self.description + " / " + str(other) elif self.symbolic_expression is not None: expr = (self.symbolic_expression) / other - descr = "( " + self.description + " ) / " + str(other) + descr = "(" + self.description + ") / " + str(other) else: expr = None descr = self.description + " / " + str(other) @@ -847,7 +847,7 @@ def __rtruediv__(self, other): descr = str(other) + " / " + self.description elif self.symbolic_expression is not None: expr = other / (self.symbolic_expression) - descr = str(other) + " / ( " + self.description + " )" + descr = str(other) + " / (" + self.description + ")" else: expr = None descr = str(other) + " / " + self.description @@ -890,7 +890,7 @@ def __pow__(self, power, modulo=None): if self.symbolic_expression is not None: expr = (self.symbolic_expression) ** power - descr = "( " + self.description + " ) to the power "+str(power) + descr = "(" + self.description + ") to the power "+str(power) elif self.symbol is not None: expr = self.symbol ** power descr = self.description + " to the power "+str(power) @@ -930,7 +930,7 @@ def __pow__(self, power, modulo=None): units = "".join(units) if self.symbolic_expression is not None: expr = (self.symbolic_expression) ** power - descr = "( " + self.description + " ) to the power "+str(power) + descr = "(" + self.description + ") to the power "+str(power) elif self.symbol is not None: expr = self.symbol ** power descr = self.description + " to the power "+str(power) From e990360e1c8c739d8c3ba163193f42fed2c34c59 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 11 Oct 2023 12:21:27 +0200 Subject: [PATCH 083/143] ParametersArray operations are now units and symbolic aware --- qgs/params/parameter.py | 182 ++++++++++++++++++++++++++++++++++------ qgs/params/params.py | 10 +-- 2 files changed, 163 insertions(+), 29 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 0b9025d..79b8646 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -255,7 +255,7 @@ def __mul__(self, other): res = float(self) * float(other) if isinstance(other, (Parameter, ScalingParameter)): - units = _join_units(self.units, other.units, '+') + units = _combine_units(self.units, other.units, '+') if self.symbolic_expression is None: if other.symbolic_expression is None: @@ -309,7 +309,7 @@ def __truediv__(self, other): res = float(self) / float(other) if isinstance(other, (ScalingParameter, Parameter)): - units = _join_units(self.units, other.units, '-') + units = _combine_units(self.units, other.units, '-') if self.symbolic_expression is None: if other.symbolic_expression is None: if self.symbol is not None and other.symbol is not None: @@ -742,7 +742,7 @@ def __mul__(self, other): res = float(self) * float(other) if isinstance(other, (Parameter, ScalingParameter)): if hasattr(other, "units"): - units = _join_units(self.units, other.units, '+') + units = _combine_units(self.units, other.units, '+') else: units = "" @@ -796,7 +796,7 @@ def __truediv__(self, other): res = float(self) / float(other) if isinstance(other, (ScalingParameter, Parameter)): - units = _join_units(self.units, other.units, '-') + units = _combine_units(self.units, other.units, '-') if self.symbolic_expression is None: if other.symbolic_expression is None: if self.symbol is not None and other.symbol is not None: @@ -948,7 +948,7 @@ class ParametersArray(np.ndarray): Parameters ---------- - values: list(float) or ~numpy.ndarray(float) or list(Parameter) or ~numpy.ndarray(Parameter) + values: list(float) or ~numpy.ndarray(float) or list(Parameter) or ~numpy.ndarray(Parameter) or list(ScalingParameter) or ~numpy.ndarray(ScalingParameter) Values of the parameter array. input_dimensional: bool, optional Specify whether the value provided is dimensional or not. Default to `True`. @@ -962,9 +962,12 @@ class ParametersArray(np.ndarray): description: str or list(str) or array(str), optional String or an iterable of strings, describing the parameters. If an iterable, should have the same length or shape as `values`. - symbol: ~sympy.core.symbol.Symbol or list(~sympy.core.symbol.Symbol) or ~numpy.ndarray(~sympy.core.symbol.Symbol), optional + symbols ~sympy.core.symbol.Symbol or list(~sympy.core.symbol.Symbol) or ~numpy.ndarray(~sympy.core.symbol.Symbol), optional A `Sympy`_ symbol or an iterable of symbols, to represent the parameters in symbolic expressions. If an iterable, should have the same length or shape as `values`. + symbolic_expressions: ~sympy.core.expr.Expr or list(~sympy.core.expr.Expr) or ~numpy.ndarray(~sympy.core.expr.Expr), optional + A `Sympy`_ expression or an iterable of expressions, to represent a relationship to other parameters. + If an iterable, should have the same length or shape as `values`. return_dimensional: bool, optional Defined if the value returned by the parameter is dimensional or not. Default to `False`. @@ -976,7 +979,7 @@ class ParametersArray(np.ndarray): """ def __new__(cls, values, input_dimensional=True, units="", scale_object=None, description="", - symbol=None, return_dimensional=False): + symbols=None, symbolic_expressions=None, return_dimensional=False): if isinstance(values, (tuple, list)): new_arr = np.empty(len(values), dtype=object) @@ -985,25 +988,36 @@ def __new__(cls, values, input_dimensional=True, units="", scale_object=None, de descr = description[i] else: descr = description - if isinstance(symbol, (tuple, list, np.ndarray)): - sy = symbol[i] + if isinstance(symbols, (tuple, list, np.ndarray)): + sy = symbols[i] + else: + sy = symbols + if isinstance(symbolic_expressions, (tuple, list, np.ndarray)): + expr = symbolic_expressions[i] else: - sy = symbol + expr = symbolic_expressions new_arr[i] = Parameter(val, input_dimensional=input_dimensional, units=units, scale_object=scale_object, description=descr, - return_dimensional=return_dimensional, symbol=sy) + return_dimensional=return_dimensional, symbol=sy, symbolic_expression=expr) else: - new_arr = np.empty_like(values, dtype=object) - for idx in np.ndindex(values.shape): - if isinstance(description, np.ndarray): - descr = description[idx] - else: - descr = description - if isinstance(symbol, np.ndarray): - sy = symbol[idx] - else: - sy = symbol - new_arr[idx] = Parameter(values[idx], input_dimensional=input_dimensional, units=units, scale_object=scale_object, description=descr, - return_dimensional=return_dimensional, symbol=sy) + if isinstance(values.flatten()[0], (Parameter, ScalingParameter)): + new_arr = values.copy() + else: + new_arr = np.empty_like(values, dtype=object) + for idx in np.ndindex(values.shape): + if isinstance(description, np.ndarray): + descr = description[idx] + else: + descr = description + if isinstance(symbols, np.ndarray): + sy = symbols[idx] + else: + sy = symbols + if isinstance(symbolic_expressions, np.ndarray): + expr = symbolic_expressions[idx] + else: + expr = symbolic_expressions + new_arr[idx] = Parameter(values[idx], input_dimensional=input_dimensional, units=units, scale_object=scale_object, description=descr, + return_dimensional=return_dimensional, symbol=sy, symbolic_expression=expr) arr = np.asarray(new_arr).view(cls) arr._input_dimensional = input_dimensional arr._return_dimensional = return_dimensional @@ -1106,8 +1120,128 @@ def _nondimensionalization(self): else: return self._conversion_factor(self._units, self._scale_object) + def __add__(self, other): + if isinstance(other, (Parameter, ScalingParameter, float, int)): + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = self[idx] + other + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + elif isinstance(other, ParametersArray): + if other.shape == self.shape: # Does not do broadcast + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = self[idx] + other[idx] + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + else: + return self + other + else: + return self + other + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + if isinstance(other, (Parameter, ScalingParameter, float, int)): + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = self[idx] - other + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + elif isinstance(other, ParametersArray): + if other.shape == self.shape: # Does not do broadcast + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = self[idx] - other[idx] + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + else: + return self - other + else: + return self - other + + def __rsub__(self, other): + if isinstance(other, (Parameter, ScalingParameter, float, int)): + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = other - self[idx] + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + elif isinstance(other, ParametersArray): + if other.shape == self.shape: # Does not do broadcast + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = other - self[idx] + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + else: + return other - self + else: + return other - self + + def __mul__(self, other): + if isinstance(other, (Parameter, ScalingParameter, float, int)): + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = self[idx] * other + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + elif isinstance(other, ParametersArray): + if other.shape == self.shape: # Does not do broadcast + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = self[idx] * other[idx] + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + else: + return self * other + else: + return self * other + + def __rmul__(self, other): + return self.__mul__(other) + + def __truediv__(self, other): + if isinstance(other, (Parameter, ScalingParameter, float, int)): + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = self[idx] / other + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + elif isinstance(other, ParametersArray): + if other.shape == self.shape: # Does not do broadcast + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = self[idx] / other[idx] + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + else: + return self / other + else: + return self / other + + def __rtruediv__(self, other): + if isinstance(other, (Parameter, ScalingParameter, float, int)): + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = other / self[idx] + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + elif isinstance(other, ParametersArray): + if other.shape == self.shape: # Does not do broadcast + res = np.empty(self.shape, dtype=object) + for idx in np.ndindex(self.shape): + res[idx] = other / self[idx] + return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + units=res[idx].units, scale_object=self._scale_object) + else: + return other / self + else: + return other / self + -def _join_units(units1, units2, operation): +def _combine_units(units1, units2, operation): ul = units1.split('][') ul[0] = ul[0][1:] ul[-1] = ul[-1][:-1] diff --git a/qgs/params/params.py b/qgs/params/params.py index 5d7cd27..0446013 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -421,7 +421,7 @@ def _create_insolation(self, values, dynamic_T=False): sy = [Symbol('C_a'+str(pos+offset)) for pos in range(dim)] self.C = ParametersArray(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True, symbol=sy) + description=d, return_dimensional=True, symbols=sy) def set_thetas(self, value, pos=None): """Function to define the spectral decomposition of the Newtonian cooling @@ -461,7 +461,7 @@ def _create_thetas(self, values): sy = [Symbol('thetas_'+str(pos+1)) for pos in range(dim)] self.thetas = ParametersArray(values, scale_object=self._scale_params, - description=d, return_dimensional=False, input_dimensional=False, symbol=sy) + description=d, return_dimensional=False, input_dimensional=False, symbols=sy) class OceanicParams(Params): @@ -588,7 +588,7 @@ def _create_insolation(self, values, dynamic_T=False): sy = [Symbol('C_go'+str(pos+offset)) for pos in range(dim)] self.C = ParametersArray(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True, symbol=sy) + description=d, return_dimensional=True, symbols=sy) class GroundParams(Params): @@ -667,7 +667,7 @@ def _create_orography(self, values): d = ["spectral component "+str(pos+1)+" of the orography" for pos in range(dim)] self.hk = ParametersArray(values, scale_object=self._scale_params, - description=d, return_dimensional=False, input_dimensional=False, symbol=values) + description=d, return_dimensional=False, input_dimensional=False, symbols=values) class GroundTemperatureParams(Params): @@ -755,7 +755,7 @@ def _create_insolation(self, values, dynamic_T=False): sy = [Symbol('C_go'+str(pos+offset)) for pos in range(dim)] self.C = ParametersArray(values, units='[W][m^-2]', scale_object=self._scale_params, - description=d, return_dimensional=True, symbol=sy) + description=d, return_dimensional=True, symbols=sy) class QgParams(Params): From fd1dc260b24bfd0d08c9a94cab2d29b278f7bf2a Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 11 Oct 2023 13:17:34 +0200 Subject: [PATCH 084/143] Corrected the way dimensionality is managed in arithmetic operations --- qgs/params/parameter.py | 82 +++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 79b8646..9e6d3fe 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -651,7 +651,7 @@ def __add__(self, other): expr = (self.symbolic_expression) + (other.symbolic_expression) descr = "(" + self.description + ") + (" + other.description + ")" - return Parameter(res, input_dimensional=self.input_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) else: @@ -664,7 +664,7 @@ def __add__(self, other): else: expr = None descr = self.description + " + " + str(other) - return Parameter(res, input_dimensional=self.input_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) @@ -705,7 +705,7 @@ def __sub__(self, other): expr = (self.symbolic_expression) - (other.symbolic_expression) descr = "(" + self.description + ") - (" + other.description + ")" - return Parameter(res, input_dimensional=self.input_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) else: @@ -718,7 +718,7 @@ def __sub__(self, other): else: expr = None descr = self.description + " - " + str(other) - return Parameter(res, input_dimensional=self.input_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) @@ -733,7 +733,7 @@ def __rsub__(self, other): else: expr = None descr = str(other) + " - " + self.description - return Parameter(res, input_dimensional=self.input_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) @@ -772,7 +772,7 @@ def __mul__(self, other): expr = (self.symbolic_expression) * (other.symbolic_expression) descr = "(" + self.description + ") * (" + other.description + ")" - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=units, symbol=None, symbolic_expression=expr) else: @@ -785,7 +785,7 @@ def __mul__(self, other): else: expr = None descr = self.description + " * " + str(other) - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) @@ -823,7 +823,7 @@ def __truediv__(self, other): expr = (self.symbolic_expression) / (other.symbolic_expression) descr = "(" + self.description + ") / (" + other.description + ")" - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=units, symbol=None, symbolic_expression=expr) else: @@ -836,7 +836,7 @@ def __truediv__(self, other): else: expr = None descr = self.description + " / " + str(other) - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) @@ -851,7 +851,7 @@ def __rtruediv__(self, other): else: expr = None descr = str(other) + " / " + self.description - return Parameter(res, input_dimensional=self.input_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) @@ -938,7 +938,7 @@ def __pow__(self, power, modulo=None): expr = None descr = self.description + " to the power "+str(power) - return Parameter(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, description=descr, units=units, scale_object=self._scale_object, symbol=None, symbolic_expression=expr) @@ -1125,15 +1125,17 @@ def __add__(self, other): res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = self[idx] + other - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) elif isinstance(other, ParametersArray): if other.shape == self.shape: # Does not do broadcast res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = self[idx] + other[idx] - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) else: return self + other else: @@ -1147,15 +1149,17 @@ def __sub__(self, other): res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = self[idx] - other - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) elif isinstance(other, ParametersArray): if other.shape == self.shape: # Does not do broadcast res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = self[idx] - other[idx] - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) else: return self - other else: @@ -1166,15 +1170,17 @@ def __rsub__(self, other): res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = other - self[idx] - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) elif isinstance(other, ParametersArray): if other.shape == self.shape: # Does not do broadcast res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = other - self[idx] - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) else: return other - self else: @@ -1185,15 +1191,17 @@ def __mul__(self, other): res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = self[idx] * other - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) elif isinstance(other, ParametersArray): if other.shape == self.shape: # Does not do broadcast res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = self[idx] * other[idx] - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) else: return self * other else: @@ -1207,15 +1215,17 @@ def __truediv__(self, other): res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = self[idx] / other - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) elif isinstance(other, ParametersArray): if other.shape == self.shape: # Does not do broadcast res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = self[idx] / other[idx] - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) else: return self / other else: @@ -1226,15 +1236,17 @@ def __rtruediv__(self, other): res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = other / self[idx] - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) elif isinstance(other, ParametersArray): if other.shape == self.shape: # Does not do broadcast res = np.empty(self.shape, dtype=object) for idx in np.ndindex(self.shape): res[idx] = other / self[idx] - return ParametersArray(res, input_dimensional=self.input_dimensional, return_dimensional=self.return_dimensional, - units=res[idx].units, scale_object=self._scale_object) + item = res[idx] + return ParametersArray(res, input_dimensional=item.return_dimensional, return_dimensional=item.return_dimensional, + units=item.units, scale_object=self._scale_object) else: return other / self else: From dff6919854ff548be0d961f3288a9aa1bfd5d292 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 11 Oct 2023 14:05:57 +0200 Subject: [PATCH 085/143] Fall back to default arithmetic operations if things go wrong --- qgs/params/parameter.py | 298 ++++++++++++++++++++++------------------ 1 file changed, 167 insertions(+), 131 deletions(-) diff --git a/qgs/params/parameter.py b/qgs/params/parameter.py index 9e6d3fe..1917307 100644 --- a/qgs/params/parameter.py +++ b/qgs/params/parameter.py @@ -135,7 +135,7 @@ def description(self): def __add__(self, other): - res = float(self) + float(other) + res = float(self) + other if isinstance(other, (Parameter, ScalingParameter)): if self.units != other.units: raise ArithmeticError("ScalingParameter class: Impossible to add two parameters with different units.") @@ -173,23 +173,26 @@ def __add__(self, other): else: return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) else: - if self.symbol is not None: - expr = self.symbol + other - descr = self.description + " + " + str(other) - elif self.symbolic_expression is not None: - expr = (self.symbolic_expression) + other - descr = "(" + self.description + ") + " + str(other) - else: - expr = None - descr = self.description + " + " + str(other) - return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + try: + if self.symbol is not None: + expr = self.symbol + other + descr = self.description + " + " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) + other + descr = "(" + self.description + ") + " + str(other) + else: + expr = None + descr = self.description + " + " + str(other) + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res def __radd__(self, other): return self.__add__(other) def __sub__(self, other): - res = float(self) - float(other) + res = float(self) - other if isinstance(other, (Parameter, ScalingParameter)): if self.units != other.units: raise ArithmeticError("ScalingParameter class: Impossible to subtract two parameters with different units.") @@ -227,33 +230,39 @@ def __sub__(self, other): else: return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) else: + try: + if self.symbol is not None: + expr = self.symbol - other + descr = self.description + " - " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) - other + descr = "(" + self.description + ") - " + str(other) + else: + expr = None + descr = self.description + " - " + str(other) + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res + + def __rsub__(self, other): + try: + res = other - float(self) if self.symbol is not None: - expr = self.symbol - other - descr = self.description + " - " + str(other) + expr = other - self.symbol + descr = str(other) + " - " + self.description elif self.symbolic_expression is not None: - expr = (self.symbolic_expression) - other - descr = "(" + self.description + ") - " + str(other) + expr = other - (self.symbolic_expression) + descr = str(other) + " - (" + self.description + ")" else: expr = None - descr = self.description + " - " + str(other) + descr = str(other) + " - " + self.description return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) - - def __rsub__(self, other): - res = float(other) - float(self) - if self.symbol is not None: - expr = other - self.symbol - descr = str(other) + " - " + self.description - elif self.symbolic_expression is not None: - expr = other - (self.symbolic_expression) - descr = str(other) + " - (" + self.description + ")" - else: - expr = None - descr = str(other) + " - " + self.description - return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res def __mul__(self, other): - res = float(self) * float(other) + res = float(self) * other if isinstance(other, (Parameter, ScalingParameter)): units = _combine_units(self.units, other.units, '+') @@ -291,23 +300,26 @@ def __mul__(self, other): else: return ScalingParameter(res, description=descr, units=units, symbol=None, symbolic_expression=expr) else: - if self.symbol is not None: - expr = self.symbol * other - descr = self.description + " * " + str(other) - elif self.symbolic_expression is not None: - expr = (self.symbolic_expression) * other - descr = "(" + self.description + ") * " + str(other) - else: - expr = None - descr = self.description + " * " + str(other) - return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + try: + if self.symbol is not None: + expr = self.symbol * other + descr = self.description + " * " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) * other + descr = "(" + self.description + ") * " + str(other) + else: + expr = None + descr = self.description + " * " + str(other) + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res def __rmul__(self, other): return self.__mul__(other) def __truediv__(self, other): - res = float(self) / float(other) + res = float(self) / other if isinstance(other, (ScalingParameter, Parameter)): units = _combine_units(self.units, other.units, '-') if self.symbolic_expression is None: @@ -343,29 +355,35 @@ def __truediv__(self, other): else: return ScalingParameter(res, description=descr, units=units, symbol=None, symbolic_expression=expr) else: + try: + if self.symbol is not None: + expr = self.symbol / other + descr = self.description + " / " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) / other + descr = "(" + self.description + ") / " + str(other) + else: + expr = None + descr = self.description + " / " + str(other) + return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res + + def __rtruediv__(self, other): + res = other / float(self) + try: if self.symbol is not None: - expr = self.symbol / other - descr = self.description + " / " + str(other) + expr = other / self.symbol + descr = str(other) + " / " + self.description elif self.symbolic_expression is not None: - expr = (self.symbolic_expression) / other - descr = "(" + self.description + ") / " + str(other) + expr = other / (self.symbolic_expression) + descr = str(other) + " / (" + self.description + ")" else: expr = None - descr = self.description + " / " + str(other) + descr = str(other) + " / " + self.description return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) - - def __rtruediv__(self, other): - res = float(other) / float(self) - if self.symbol is not None: - expr = other / self.symbol - descr = str(other) + " / " + self.description - elif self.symbolic_expression is not None: - expr = other / (self.symbolic_expression) - descr = str(other) + " / (" + self.description + ")" - else: - expr = None - descr = str(other) + " / " + self.description - return ScalingParameter(res, description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res def __pow__(self, power, modulo=None): @@ -618,7 +636,7 @@ def _nondimensionalization(self): def __add__(self, other): - res = float(self) + float(other) + res = float(self) + other if isinstance(other, (Parameter, ScalingParameter)): if isinstance(other, Parameter) and self.return_dimensional != other.return_dimensional: raise ArithmeticError("Parameter class: Impossible to subtract a dimensional parameter with a non-dimensional one.") @@ -655,25 +673,28 @@ def __add__(self, other): return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) else: - if self.symbol is not None: - expr = self.symbol + other - descr = self.description + " + " + str(other) - elif self.symbolic_expression is not None: - expr = (self.symbolic_expression) + other - descr = "(" + self.description + ") + " + str(other) - else: - expr = None - descr = self.description + " + " + str(other) - return Parameter(res, input_dimensional=self.return_dimensional, - return_dimensional=self.return_dimensional, scale_object=self._scale_object, - description=descr, units=self.units, symbol=None, symbolic_expression=expr) + try: + if self.symbol is not None: + expr = self.symbol + other + descr = self.description + " + " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) + other + descr = "(" + self.description + ") + " + str(other) + else: + expr = None + descr = self.description + " + " + str(other) + return Parameter(res, input_dimensional=self.return_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res def __radd__(self, other): return self.__add__(other) def __sub__(self, other): - res = float(self) - float(other) + res = float(self) - other if isinstance(other, (Parameter, ScalingParameter)): if isinstance(other, Parameter) and self.return_dimensional != other.return_dimensional: raise ArithmeticError("Parameter class: Impossible to subtract a dimensional parameter with a non-dimensional one.") @@ -709,37 +730,43 @@ def __sub__(self, other): return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) else: + try: + if self.symbol is not None: + expr = self.symbol - other + descr = self.description + " - " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) - other + descr = "(" + self.description + ") - " + str(other) + else: + expr = None + descr = self.description + " - " + str(other) + return Parameter(res, input_dimensional=self.return_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res + + def __rsub__(self, other): + res = other - float(self) + try: if self.symbol is not None: - expr = self.symbol - other - descr = self.description + " - " + str(other) + expr = other - self.symbol + descr = str(other) + " - " + self.description elif self.symbolic_expression is not None: - expr = (self.symbolic_expression) - other - descr = "(" + self.description + ") - " + str(other) + expr = other - (self.symbolic_expression) + descr = str(other) + " - (" + self.description + ")" else: expr = None - descr = self.description + " - " + str(other) + descr = str(other) + " - " + self.description return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, scale_object=self._scale_object, description=descr, units=self.units, symbol=None, symbolic_expression=expr) - - def __rsub__(self, other): - res = float(other) - float(self) - if self.symbol is not None: - expr = other - self.symbol - descr = str(other) + " - " + self.description - elif self.symbolic_expression is not None: - expr = other - (self.symbolic_expression) - descr = str(other) + " - (" + self.description + ")" - else: - expr = None - descr = str(other) + " - " + self.description - return Parameter(res, input_dimensional=self.return_dimensional, - return_dimensional=self.return_dimensional, scale_object=self._scale_object, - description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res def __mul__(self, other): - res = float(self) * float(other) + res = float(self) * other if isinstance(other, (Parameter, ScalingParameter)): if hasattr(other, "units"): units = _combine_units(self.units, other.units, '+') @@ -776,25 +803,28 @@ def __mul__(self, other): scale_object=self._scale_object, description=descr, units=units, symbol=None, symbolic_expression=expr) else: - if self.symbol is not None: - expr = self.symbol * other - descr = self.description + " * " + str(other) - elif self.symbolic_expression is not None: - expr = (self.symbolic_expression) * other - descr = "(" + self.description + ") * " + str(other) - else: - expr = None - descr = self.description + " * " + str(other) - return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=descr, units=self.units, symbol=None, - symbolic_expression=expr) + try: + if self.symbol is not None: + expr = self.symbol * other + descr = self.description + " * " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) * other + descr = "(" + self.description + ") * " + str(other) + else: + expr = None + descr = self.description + " * " + str(other) + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=descr, units=self.units, symbol=None, + symbolic_expression=expr) + except: + return res def __rmul__(self, other): return self.__mul__(other) def __truediv__(self, other): - res = float(self) / float(other) + res = float(self) / other if isinstance(other, (ScalingParameter, Parameter)): units = _combine_units(self.units, other.units, '-') if self.symbolic_expression is None: @@ -827,33 +857,39 @@ def __truediv__(self, other): scale_object=self._scale_object, description=descr, units=units, symbol=None, symbolic_expression=expr) else: + try: + if self.symbol is not None: + expr = self.symbol / other + descr = self.description + " / " + str(other) + elif self.symbolic_expression is not None: + expr = (self.symbolic_expression) / other + descr = "(" + self.description + ") / " + str(other) + else: + expr = None + descr = self.description + " / " + str(other) + return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, + scale_object=self._scale_object, description=descr, units=self.units, symbol=None, + symbolic_expression=expr) + except: + return res + + def __rtruediv__(self, other): + res = other / float(self) + try: if self.symbol is not None: - expr = self.symbol / other - descr = self.description + " / " + str(other) + expr = other / self.symbol + descr = str(other) + " / " + self.description elif self.symbolic_expression is not None: - expr = (self.symbolic_expression) / other - descr = "(" + self.description + ") / " + str(other) + expr = other / (self.symbolic_expression) + descr = str(other) + " / (" + self.description + ")" else: expr = None - descr = self.description + " / " + str(other) - return Parameter(res, input_dimensional=self.return_dimensional, return_dimensional=self.return_dimensional, - scale_object=self._scale_object, description=descr, units=self.units, symbol=None, - symbolic_expression=expr) - - def __rtruediv__(self, other): - res = float(other) / float(self) - if self.symbol is not None: - expr = other / self.symbol - descr = str(other) + " / " + self.description - elif self.symbolic_expression is not None: - expr = other / (self.symbolic_expression) - descr = str(other) + " / (" + self.description + ")" - else: - expr = None - descr = str(other) + " / " + self.description - return Parameter(res, input_dimensional=self.return_dimensional, - return_dimensional=self.return_dimensional, scale_object=self._scale_object, - description=descr, units=self.units, symbol=None, symbolic_expression=expr) + descr = str(other) + " / " + self.description + return Parameter(res, input_dimensional=self.return_dimensional, + return_dimensional=self.return_dimensional, scale_object=self._scale_object, + description=descr, units=self.units, symbol=None, symbolic_expression=expr) + except: + return res def __pow__(self, power, modulo=None): From 1eb29f310d7d2654720a4fa960b476bb770eb594 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 11 Oct 2023 15:05:14 +0200 Subject: [PATCH 086/143] Adapted the symbolic QgTensor to the new paramters symbolics --- qgs/tensors/symbolic_qgtensor.py | 291 +++++++++++-------------------- 1 file changed, 102 insertions(+), 189 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 44dd583..a47acf7 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -147,93 +147,6 @@ def _deltaT_g(self, i): """ return i + self.params.variables_range[1] + 1 - # TODO: Im not happy with having these set of properties in two places, one for numerics and one for symbolic. This should be combined, or at least put in the parameter section somewhere. - # Jonathan: Ok I will think about it. Maybe we can add a new properties for parameters to return symbolic expression if it exists. - @property - def LR(self): - if self.params.oceanic_params.gp is None or self.params.oceanic_params.h is None: - return None - else: - return sy.sqrt(self.params.oceanic_params.gp.symbol * self.params.oceanic_params.h.symbol) / self.params.scale_params.f0.symbol - - @property - def G(self): - return -self.params.scale_params.L.symbol ** 2 / self.LR ** 2 - - @property - def Cpgo(self): - if self.params.gotemperature_params.C is None: - return None - else: - C = ImmutableSparseNDimArray(self.params.gotemperature_params.C.symbols) - return C / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) * self.params.rr.symbol / (self.params.scale_params.f0.symbol ** 2 * self.params.scale_params.L.symbol ** 2) - - @property - def Lpgo(self): - if self.params.atemperature_params.hlambda is None or self.params.gotemperature_params.gamma is None: - return None - else: - return self.params.atemperature_params.hlambda.symbol / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) - - @property - def Cpa(self): - if self.params.atemperature_params.C is None: - return None - else: - C = ImmutableSparseNDimArray(self.params.atemperature_params.C.symbols) - return C / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) * self.params.rr.symbol / (self.params.scale_params.f0.symbol ** 2 * self.params.scale_params.L.symbol ** 2) / 2 - - @property - def Lpa(self): - if self.params.atemperature_params.hlambda is None or self.params.atemperature_params.gamma is None: - return None - else: - return self.params.atemperature_params.hlambda.symbol / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) - - @property - def sbpgo(self): - if self.params.gotemperature_params.T0 is None: - None - else: - return 4 * self.params.sb.symbol * self.params.gotemperature_params.T0.symbol ** 3 / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) - - @property - def sbpa(self): - if self.params.atemperature_params.T0 is None: - return None - else: - return 8 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.atemperature_params.T0.symbol ** 3 / (self.params.gotemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) - - @property - def LSBpgo(self): - if self.params.gotemperature_params.T0 is None: - None - else: - return 2 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.gotemperature_params.T0.symbol ** 3 / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) - - @property - def LSBpa(self): - if self.params.atemperature_params.T0 is None: - return None - else: - return 8 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.atemperature_params.T0.symbol ** 3 / (self.params.atemperature_params.gamma.symbol * self.params.scale_params.f0.symbol) - - @property - def T4sbpgo(self): - return self.params.sb.symbol * self.params.scale_params.L.symbol ** 6 * self.params.scale_params.f0.symbol ** 5 / (self.params.gotemperature_params.gamma.symbol * self.params.rr.symbol ** 3) - - @property - def T4sbpa(self): - return 16 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.scale_params.L.symbol ** 6 * self.params.scale_params.f0.symbol ** 5 / (self.params.gotemperature_params.gamma.symbol * self.params.rr.symbol ** 3) - - @property - def T4LSBpgo(self): - return 0.5 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.scale_params.L.symbol ** 6 * self.params.scale_params.f0.symbol ** 5 / (self.params.atemperature_params.gamma.symbol * self.params.rr.symbol ** 3) - - @property - def T4LSBpa(self): - return 16 * self.params.atemperature_params.eps.symbol * self.params.sb.symbol * self.params.scale_params.L.symbol ** 6 * self.params.scale_params.f0.symbol ** 5 / (self.params.atemperature_params.gamma.symbol * self.params.rr.symbol ** 3) - def _compute_tensor_dicts(self): if self.params is None: return None @@ -302,7 +215,7 @@ def _compute_tensor_dicts(self): M_psio = dict() for i in range(offset, nvar[3]): for j in range(offset, nvar[3]): - M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.G * bips.U(i, j) + M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.params.G.symbolic_expression * bips.U(i, j) M_psio = ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) M_psio = M_psio.inverse() @@ -369,8 +282,8 @@ def _compute_tensor_dicts(self): # theta_a part a_theta_mult_u = symbolic_tensordot(a_theta, aips._u, axes=1) - if self.Cpa is not None: - val_Cpa = symbolic_tensordot(a_theta_mult_u, self.Cpa, axes=1) + if self.params.Cpa is not None: + val_Cpa = symbolic_tensordot(a_theta_mult_u, ImmutableSparseNDimArray(self.params.Cpa.symbolic_expressions), axes=1) if atp.hd is not None and atp.thetas is not None: thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbols) @@ -398,7 +311,7 @@ def _compute_tensor_dicts(self): a_theta_mult_s = symbolic_tensordot(a_theta, aips._s, axes=1) for i in range(nvar[1]): - if self.Cpa is not None: + if self.params.Cpa is not None: sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), -val_Cpa[i]) if atp.hd is not None and atp.thetas is not None: @@ -428,10 +341,10 @@ def _compute_tensor_dicts(self): sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_a(j), self._theta_a(ko)), a_theta_mult_g[i, j, k]) for j in range(nvar[1]): - if self.Lpa is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.sc.symbol * self.Lpa) - if self.LSBpa is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * self.LSBpa) + if self.params.Lpa is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.sc.symbol * self.params.Lpa.symbolic_expression) + if self.params.LSBpa is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * self.params.LSBpa.symbolic_expression) if atp.hd is not None: sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), a_theta_mult_u[i, j] * atp.hd) @@ -440,18 +353,18 @@ def _compute_tensor_dicts(self): for j in range(nvar[2]): sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), -a_theta_mult_d[i, j] * ap.sig0.symbol * ap.kd.symbol / 2) - if self.Lpa is not None: + if self.params.Lpa is not None: for j in range(nvar[3]): - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.Lpa / 2) - if self.LSBpgo is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.LSBpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.params.Lpa.symbolic_expression / 2) + if self.params.LSBpgo is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), -a_theta_mult_s[i, j] * self.params.LSBpgo.symbolic_expression) if ground_temp: - if self.Lpa is not None: + if self.params.Lpa is not None: for j in range(nvar[2]): - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.Lpa / 2) - if self.LSBpgo is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.LSBpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.params.Lpa.symbolic_expression / 2) + if self.params.LSBpgo is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), -a_theta_mult_s[i, j] * self.params.LSBpgo.symbolic_expression) if ocean: # psi_o part @@ -477,7 +390,7 @@ def _compute_tensor_dicts(self): # deltaT_o part U_inv_mult_W = symbolic_tensordot(U_inv, bips._W, axes=1) - U_inv_mult_W_Cpgo = symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) + U_inv_mult_W_Cpgo = symbolic_tensordot(U_inv_mult_W, ImmutableSparseNDimArray(self.params.Cpgo.symbolic_expressions), axes=1) U_inv_mult_O = symbolic_tensordot(U_inv, bips._O[:, offset:, offset:], axes=1) @@ -485,14 +398,14 @@ def _compute_tensor_dicts(self): sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), U_inv_mult_W_Cpgo[i]) for j in range(nvar[1]): - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.Lpgo) - if self.sbpa is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.params.Lpgo.symbolic_expression) + if self.params.sbpa is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.params.sbpa.symbolic_expression) for j in range(nvar[3]): - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) - if self.sbpgo is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.sbpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.params.Lpgo.symbolic_expression * _kronecker_delta(i, j)) + if self.params.sbpgo is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.params.sbpgo.symbolic_expression * _kronecker_delta(i, j)) for j in range(nvar[2]): for k in range(nvar[2]): @@ -502,19 +415,19 @@ def _compute_tensor_dicts(self): # deltaT_g part if ground_temp: U_inv_mult_W = symbolic_tensordot(U_inv, bips._W, axes=1) - U_inv_mult_W_Cpgo = symbolic_tensordot(U_inv_mult_W, self.Cpgo, axes=1) + U_inv_mult_W_Cpgo = symbolic_tensordot(U_inv_mult_W, ImmutableSparseNDimArray(self.params.Cpgo.symbolic_expressions), axes=1) for i in range(nvar[2]): sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i]) for j in range(nvar[1]): - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.Lpgo) - if self.sbpa is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.sbpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * 2 * atp.sc.symbol * self.params.Lpgo.symbolic_expression) + if self.params.sbpa is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), U_inv_mult_W[i, j] * self.params.sbpa.symbolic_expression) for j in range(nvar[2]): - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) - if self.sbpgo is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.sbpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.params.Lpgo.symbolic_expression * _kronecker_delta(i, j)) + if self.params.sbpgo is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.params.sbpgo.symbolic_expression * _kronecker_delta(i, j)) else: # psi_a part @@ -562,11 +475,11 @@ def _compute_tensor_dicts(self): # theta_a part for i in range(nvar[1]): - if self.Cpa is not None: + if self.params.Cpa is not None: val = 0 for jj in range(nvar[1]): for kk in range(nvar[1]): - val -= a_theta[i, jj] * aips.u(jj, kk) * self.Cpa[kk] + val -= a_theta[i, jj] * aips.u(jj, kk) * self.params.Cpa.symbolic_expressions[kk] sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), 0, 0), val) @@ -630,11 +543,11 @@ def _compute_tensor_dicts(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips.u(jj, j) - if self.Lpa is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.sc.symbol * self.Lpa) + if self.params.Lpa is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.sc.symbol * self.params.Lpa.symbolic_expression) - if self.LSBpa is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.LSBpa) + if self.params.LSBpa is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * self.params.LSBpa.symbolic_expression) if atp.hd is not None: sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), 0), val * atp.hd.symbol) @@ -648,24 +561,24 @@ def _compute_tensor_dicts(self): sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._psi_o(j), 0), val * ap.sig0.symbol * ap.kd.symbol / 2) - if self.Lpa is not None: + if self.params.Lpa is not None: for j in range(nvar[3]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.Lpa / 2) - if self.LSBpgo is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.LSBpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.params.Lpa.symbolic_expression / 2) + if self.params.LSBpgo is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), 0), val * self.params.LSBpgo.symbolic_expression) if ground_temp: - if self.Lpa is not None: + if self.params.Lpa is not None: for j in range(nvar[2]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.s(jj, j) - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.Lpa / 2) - if self.LSBpgo is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.LSBpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.params.Lpa.symbolic_expression / 2) + if self.params.LSBpgo is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), 0), val * self.params.LSBpgo.symbolic_expression) if ocean: # psi_o part @@ -703,7 +616,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): for kk in range(nvar[3]): - val += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] + val += U_inv[i, kk] * bips.W(kk, jj) * self.params.Cpgo.symbolic_expressions[jj] sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), 0, 0), val) @@ -711,14 +624,14 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips.W(jj, j) - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) - if self.sbpa is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * self.sbpa) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.params.Lpgo.symbolic_expression) + if self.params.sbpa is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), 0), val * self.params.sbpa.symbolic_expression) for j in range(nvar[3]): - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.Lpgo * _kronecker_delta(i, j)) - if self.sbpgo is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.sbpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.params.Lpgo.symbolic_expression * _kronecker_delta(i, j)) + if self.params.sbpgo is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), 0), - self.params.sbpgo.symbolic_expression * _kronecker_delta(i, j)) for j in range(nvar[2]): for k in range(offset, nvar[3]): @@ -735,7 +648,7 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[1]): for kk in range(nvar[2]): - val += U_inv[i, kk] * bips.W(kk, jj) * self.Cpgo[jj] + val += U_inv[i, kk] * bips.W(kk, jj) * self.params.Cpgo.symbolic_expressions[jj] sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), val) @@ -743,15 +656,15 @@ def _compute_tensor_dicts(self): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips.W(jj, j) - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.Lpgo) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * 2 * atp.sc.symbol * self.params.Lpgo.symbolic_expression) - if self.sbpa is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.sbpa) + if self.params.sbpa is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), 0), val * self.params.sbpa.symbolic_expression) for j in range(nvar[2]): - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.Lpgo * _kronecker_delta(i, j)) - if self.sbpgo is not None: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.sbpgo * _kronecker_delta(i, j)) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.params.Lpgo.symbolic_expression * _kronecker_delta(i, j)) + if self.params.sbpgo is not None: + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), 0), - self.params.sbpgo.symbolic_expression * _kronecker_delta(i, j)) return sy_arr_dic @@ -1043,40 +956,40 @@ def _compute_stored_full_dict(self): sy_arr_dic = dict() # theta_a part for i in range(nvar[1]): - if self.T4LSBpa is not None: + if self.params.T4LSBpa is not None: j = k = ell = 0 for m in range(nvar[1]): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips._z[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4LSBpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.params.T4LSBpa.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4LSBpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.params.T4LSBpa.symbolic_expression * val) if ocean: - if self.T4LSBpgo is not None: + if self.params.T4LSBpgo is not None: j = k = ell = 0 for m in range(nvar[3]): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips._v[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -self.T4LSBpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -self.params.T4LSBpgo.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.T4LSBpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.params.T4LSBpgo.symbolic_expression * val) if ground_temp: - if self.T4LSBpgo is not None: + if self.params.T4LSBpgo is not None: j = k = ell = 0 for m in range(nvar[2]): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips._v[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.T4LSBpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.params.T4LSBpgo.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.T4LSBpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.params.T4LSBpgo.symbolic_expression * val) if ocean: # delta_T part @@ -1087,18 +1000,18 @@ def _compute_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.params.T4LSBpa.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.params.T4LSBpa.symbolic_expression * val) for m in range(nvar[3]): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), - self.T4sbpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), - self.params.T4LSBpgo.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.T4sbpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.params.T4LSBpgo.symbolic_expression * val) if ground_temp: # deltaT_g part @@ -1109,18 +1022,18 @@ def _compute_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.T4sbpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.params.T4LSBpa.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.T4sbpa * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.params.T4LSBpa.symbolic_expression * val) for m in range(nvar[2]): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.T4sbpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.params.T4LSBpgo.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.T4sbpgo * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.params.T4LSBpgo.symbolic_expression * val) return sy_arr_dic @@ -1175,40 +1088,40 @@ def _compute_non_stored_full_dict(self): # theta_a part for i in range(nvar[1]): - if self.T4LSBpa is not None: + if self.params.T4LSBpa is not None: j = k = ell = 0 for m in range(nvar[1]): val = 0 for jj in range(nvar[1]): val += a_theta[i, jj] * aips.z(jj, j, k, ell, m) if m == 0: - sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4LSBpa * val + sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4LSBpa.symbolic_expression * val else: - sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4LSBpa * val + sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.params.T4LSBpa.symbolic_expression * val if ocean: - if self.T4LSBpgo is not None: + if self.params.T4LSBpgo is not None: j = k = ell = 0 for m in range(nvar[3]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) if m == 0: - sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.params.T4LSBpgo.symbolic_expression * val else: - sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.params.T4LSBpgo.symbolic_expression * val if ground_temp: - if self.T4LSBpgo is not None: + if self.params.T4LSBpgo is not None: j = k = ell = 0 for m in range(nvar[2]): val = 0 for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) if m == 0: - sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.params.T4LSBpgo.symbolic_expression * val else: - sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.params.T4LSBpgo.symbolic_expression * val if ocean: @@ -1221,18 +1134,18 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips.Z(jj, j, k, ell, m) if m == 0: - sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4LSBpa.symbolic_expression * val else: - sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4sbpa * val + sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.params.T4LSBpa.symbolic_expression * val for m in range(nvar[3]): val = 0 for jj in range(nvar[3]): val -= U_inv[i, jj] * bips.V(jj, j, k, ell, m) if m == 0: - sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4sbpgo * val + sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.params.T4LSBpgo.symbolic_expression * val else: - sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.T4sbpgo * val + sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.params.T4LSBpgo.symbolic_expression * val # deltaT_g part if ground_temp: @@ -1244,18 +1157,18 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4LSBpa.symbolic_expression * val else: - sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.T4sbpa * val + sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.params.T4LSBpa.symbolic_expression * val for m in range(nvar[2]): val = 0 for jj in range(nvar[2]): val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4sbpgo * val + sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.params.T4LSBpgo.symbolic_expression * val else: - sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.T4sbpgo * val + sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.params.T4LSBpgo.symbolic_expression * val return sy_arr_dic @@ -1374,7 +1287,7 @@ def _compute_non_stored_full_dict(self): # theta_a part for i in range(nvar[1]): - if self.T4LSBpa is not None: + if self.params.T4LSBpa is not None: for j in range(nvar[1]): for k in range(nvar[1]): for ell in range(nvar[1]): @@ -1383,10 +1296,10 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[1]): val += a_theta[i, jj] * aips.z(jj, j, k, ell, m) - sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4LSBpa * val + sy_arr_dic[(self._theta_a(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4LSBpa.symbolic_expression * val if ocean: - if self.T4LSBpgo is not None: + if self.params.T4LSBpgo is not None: for j in range(nvar[3]): for k in range(nvar[3]): for ell in range(nvar[3]): @@ -1395,10 +1308,10 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) - sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.params.T4LSBpgo.symbolic_expression * val if ground_temp: - if self.T4LSBpgo is not None: + if self.params.T4LSBpgo is not None: for j in range(nvar[2]): for k in range(nvar[2]): for ell in range(nvar[2]): @@ -1407,7 +1320,7 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[1]): val -= a_theta[i, jj] * aips.v(jj, j, k, ell, m) - sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4LSBpgo * val + sy_arr_dic[(self._theta_a(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.params.T4LSBpgo.symbolic_expression * val if ocean: @@ -1421,7 +1334,7 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips.Z(jj, j, k, ell, m) - sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4LSBpa.symbolic_expression * val for j in range(nvar[3]): for k in range(nvar[3]): @@ -1431,7 +1344,7 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[3]): val -= U_inv[i, jj] * bips.V(jj, j, k, ell, m) - sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.T4sbpgo * val + sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.params.T4LSBpgo.symbolic_expression * val # deltaT_g part if ground_temp: @@ -1444,7 +1357,7 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] - sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.T4sbpa * val + sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4LSBpa.symbolic_expression * val for j in range(nvar[2]): for k in range(nvar[2]): @@ -1454,7 +1367,7 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[2]): val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] - sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.T4sbpgo * val + sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.params.T4LSBpgo.symbolic_expression * val return sy_arr_dic From f7a9e438cca8e3ae84fe5c16d4cde23a0c5ddb4d Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 18 Oct 2023 11:17:19 +0200 Subject: [PATCH 087/143] Moved the default AUTO templates inside the symbolic output module Still need to be tested. --- model_test/test_aotensor_sym.py | 4 +- model_test/test_aotensor_sym_dynT.py | 4 +- .../Symbolic Output-Linear-ground test.ipynb | 2 +- notebooks/Symbolic Output-Linear.ipynb | 4 +- notebooks/Symbolic Output-dynamic_T.ipynb | 2 +- qgs/functions/symbolic_mul.py | 116 +++-- qgs/functions/symbolic_output.py | 435 +++++++++++++++--- qgs/tensors/symbolic_qgtensor.py | 14 +- 8 files changed, 468 insertions(+), 113 deletions(-) diff --git a/model_test/test_aotensor_sym.py b/model_test/test_aotensor_sym.py index 4cf5c9f..d92c446 100644 --- a/model_test/test_aotensor_sym.py +++ b/model_test/test_aotensor_sym.py @@ -16,7 +16,7 @@ from qgs.params.params import QgParams from qgs.inner_products import symbolic from qgs.tensors.qgtensor import QgsTensor -from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear +from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor from model_test.test_base_symbolic import TestBaseSymbolic @@ -57,7 +57,7 @@ def symbolic_outputs(self, output_func=None): aip.connect_to_ocean(oip) - sym_aotensor = SymbolicTensorLinear(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) + sym_aotensor = SymbolicQgsTensor(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) subbed_tensor = sym_aotensor.sub_tensor() diff --git a/model_test/test_aotensor_sym_dynT.py b/model_test/test_aotensor_sym_dynT.py index 9140d90..baa16d3 100644 --- a/model_test/test_aotensor_sym_dynT.py +++ b/model_test/test_aotensor_sym_dynT.py @@ -16,7 +16,7 @@ from qgs.params.params import QgParams from qgs.inner_products import symbolic from qgs.tensors.qgtensor import QgsTensorDynamicT -from qgs.tensors.symbolic_qgtensor import SymbolicTensorDynamicT +from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensorDynamicT from model_test.test_base_symbolic import TestBaseSymbolic @@ -51,7 +51,7 @@ def symbolic_outputs(self, output_func=None): aip.connect_to_ocean(oip) - sym_aotensor = SymbolicTensorDynamicT(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) + sym_aotensor = SymbolicQgsTensorDynamicT(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) subbed_tensor = sym_aotensor.sub_tensor() diff --git a/notebooks/Symbolic Output-Linear-ground test.ipynb b/notebooks/Symbolic Output-Linear-ground test.ipynb index 4843919..5fd0f41 100644 --- a/notebooks/Symbolic Output-Linear-ground test.ipynb +++ b/notebooks/Symbolic Output-Linear-ground test.ipynb @@ -60,7 +60,7 @@ "source": [ "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, GroundSymbolicInnerProducts\n", "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", + "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT\n", "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" ] }, diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 6c85eb6..478536f 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -94,7 +94,7 @@ "source": [ "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", + "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT\n", "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" ] }, @@ -156,7 +156,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, _parameter_values" + "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, _parameter_values" ] }, { diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index e96c57a..bb8a6ed 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -44,7 +44,7 @@ "source": [ "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT\n", + "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT\n", "from qgs.tensors.symbolic_qgtensor import _shift_dict_keys, _add_to_dict" ] }, diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py index ed47a61..a2cfc04 100644 --- a/qgs/functions/symbolic_mul.py +++ b/qgs/functions/symbolic_mul.py @@ -1,4 +1,13 @@ -import sympy as sy +""" + Symbolic matrix operation module + ================================ + + This module supports operations and functions on the symbolic sparse tensors defined in + the :class:`~.tensors.symbolic_qgtensor.SymbolicQgsTensor` objects. + +""" + +from sympy import tensorproduct, tensorcontraction def add_to_dict(dic, loc, value): @@ -18,22 +27,21 @@ def add_to_dict(dic, loc, value): def symbolic_tensordot(a, b, axes=2): """Compute tensor dot product along specified axes of two sympy symbolic arrays - This is based on numpy.tensordot + This is based on ~numpy.tensordot . Parameters ---------- - a, b: sympy arrays - Tensors to "dot" + a, b: Sympy arrays or tensors + Tensors to take the dot product of. axes: int - * integer_like - If an int N, sum over the last N axes of `a` and the first N axes - of `b` in order. The sizes of the corresponding axes must match. + Sum over the last `axes` axes of `a` and the first `axes` axes + of `b` in order. The sizes of the corresponding axes must match. Returns ------- output: sympy tensor - The tensor dot product of the input + The tensor dot product of the input. """ as_ = a.shape @@ -43,9 +51,9 @@ def symbolic_tensordot(a, b, axes=2): b_com = [nda+i for i in range(axes)] sum_cols = tuple(a_com + b_com) - prod = sy.tensorproduct(a, b) + prod = tensorproduct(a, b) - return sy.tensorcontraction(prod, sum_cols) + return tensorcontraction(prod, sum_cols) def symbolic_sparse_mult2(dic, vec_a): @@ -54,15 +62,18 @@ def symbolic_sparse_mult2(dic, vec_a): Parameters ---------- - dic: dict(Sympy.Symbol) - A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the tensor. - vec_a: list(Sympy.Symbol) - The list :math:`a_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + dic: dict(~sympy.core.symbol.Symbol) + A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the + tensor. + vec_a: list(~sympy.core.symbol.Symbol) + The list :math:`a_k` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). Returns ------- res: dict # Jonathan: dict of what ? - The matrix :math:`A_{i,j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. + The matrix :math:`A_{i,j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), + contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. """ res = dict() @@ -80,18 +91,22 @@ def symbolic_sparse_mult3(dic, vec_a, vec_b): Parameters ---------- - dic: Dict(Sympy.Symbol) - A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the tensor. - vec_a: List(Sympy.Symbol) - The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). - vec_b: List(Sympy.Symbol) - The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + dic: dict(~sympy.core.symbol.Symbol) + A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the + tensor. + vec_a: list(~sympy.core.symbol.Symbol) + The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_b: list(~sympy.core.symbol.Symbol) + The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). Returns ------- - res: List(Sympy.Symbol) - The vector :math:`v_i`, of shape (:attr:`~.params.QgParams.ndim` + 1,), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. + res: list(~sympy.core.symbol.Symbol) + The vector :math:`v_i`, of shape (:attr:`~.params.QgParams.ndim` + 1,), contained in a dictionary, where + the keys are the tensor coordinates, and the values are the tensor values. """ res = dict() @@ -111,19 +126,24 @@ def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): Parameters ---------- - dic: Dict(Sympy.Symbol) - A dictionary where they keys are a tuple of 5 elements which are the coordinates of the tensor values, which are contained in the dictionary values. - vec_a: List(Sympy.Symbol) - The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). - vec_b: List(Sympy.Symbol) - The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). - vec_c: List(Sympy.Symbol) - The list :math:`c_l` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + dic: dict(~sympy.core.symbol.Symbol) + A dictionary where they keys are a tuple of 5 elements which are the coordinates of the tensor values, + which are contained in the dictionary values. + vec_a: list(~sympy.core.symbol.Symbol) + The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_b: list(~sympy.core.symbol.Symbol) + The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_c: list(~sympy.core.symbol.Symbol) + The list :math:`c_l` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). Returns ------- - res: List(Sympy.Symbol) - The matrix :math:`A_{i, j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. + res: list(~sympy.core.symbol.Symbol) + The matrix :math:`A_{i, j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), + contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. """ res = dict() @@ -141,21 +161,27 @@ def symbolic_sparse_mult5(dic, vec_a, vec_b, vec_c, vec_d): Parameters ---------- - dic: Dict(Sympy.Symbol) - A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the tensor. - vec_a: List(Sympy.Symbol) - The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). - vec_b: List(Sympy.Symbol) - The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). - vec_c: List(Sympy.Symbol) - The list :math:`c_l` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). - vec_d: List(Sympy.Symbol) - The list :math:`d_m` to contract the tensor with entries of Sympy Symbols. Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + dic: dict(~sympy.core.symbol.Symbol) + A dictionary whose keys are the coordinates of the tensor, and the dictionary values are the values of the + tensor. + vec_a: list(~sympy.core.symbol.Symbol) + The list :math:`a_j` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_b: list(~sympy.core.symbol.Symbol) + The list :math:`b_k` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_c: list(~sympy.core.symbol.Symbol) + The list :math:`c_l` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). + vec_d: list(~sympy.core.symbol.Symbol) + The list :math:`d_m` to contract the tensor with entries of Sympy Symbols. + Must be of shape (:attr:`~.params.QgParams.ndim` + 1,). Returns ------- - res: List(Sympy.Symbol) - The vector :math:`v_i`, of shape (:attr:`~.params.QgParams.ndim` + 1,), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. + res: list(~sympy.core.symbol.Symbol) + The vector :math:`v_i`, of shape (:attr:`~.params.QgParams.ndim` + 1,), contained in a dictionary, + where the keys are the tensor coordinates, and the values are the tensor values. """ res = dict() diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 035e06f..f759ceb 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -1,12 +1,21 @@ -# Jonathan: Needs a module description here +""" + Symbolic output module + ====================== + + This module provides functions to create a symbolic representation of the tendencies functions of the model + in various languages and for various external software. + +""" import numpy as np from sympy import Symbol, lambdify import warnings -from qgs.functions.symbolic_mul import symbolic_sparse_mult2, symbolic_sparse_mult3, symbolic_sparse_mult4, symbolic_sparse_mult5 -from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, GroundSymbolicInnerProducts -from qgs.tensors.symbolic_qgtensor import SymbolicTensorLinear, SymbolicTensorDynamicT, SymbolicTensorT4 +from qgs.functions.symbolic_mul import symbolic_sparse_mult2, symbolic_sparse_mult3, symbolic_sparse_mult4, \ + symbolic_sparse_mult5 +from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts, \ + GroundSymbolicInnerProducts +from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT, SymbolicQgsTensorT4 import os @@ -31,7 +40,9 @@ } -def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables=None, language='python', return_inner_products=False, return_jacobian=False, return_symbolic_eqs=False, return_symbolic_qgtensor=False): +def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, continuation_variables=None, + language='python', return_inner_products=False, return_jacobian=False, + return_symbolic_eqs=False, return_symbolic_qgtensor=False): """Function to output the raw symbolic functions of the qgs model. Parameters @@ -47,7 +58,8 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) The variables to not substitute and to leave in the equations, if None no variables are substituted language: str - Options for the output language syntax: 'python', 'julia', 'fortran', 'auto', 'mathematica' + Options for the output language syntax: 'python', 'julia', 'fortran', 'auto', 'mathematica'. + Default to `python`. return_inner_products: bool If `True`, return the inner products of the model. Default to `False`. return_jacobian: bool @@ -60,12 +72,12 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con Returns ------- funcs: str - The substituted functions in the language syntax specified, as a string - dict_eq_simplified: symbolic equations - Dictionary of the substituted Jacobian matrix + The substituted functions in the language syntax specified, as a string. + dict_eq_simplified: dict(~sympy.core.expr.Expr) + Dictionary of the substituted Jacobian matrix. inner_products: tuple(SymbolicAtmosphericInnerProducts, SymbolicOceanicInnerProducts, SymbolicGroundInnerProducts) If `return_inner_products` is `True`, the inner products of the system. - eq_simplified: Symbolic equations + eq_simplified: dict(~sympy.core.expr.Expr) If `return_symbolic_eqs` is `True`, dictionary of the model tendencies symbolic functions. agotensor: SymbolicQgsTensor If `return_symbolic_qgtensor` is `True`, the symbolic tendencies tensor of the system. @@ -84,7 +96,8 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con pass if not make_ip_subs: - warnings.warn("Calculating inner products symbolically, as the variable 'n' has been specified as a variable, this takes several minutes.") + warnings.warn("Calculating inner products symbolically, as the variable 'n' has been specified as a variable, " + "this takes several minutes.") if params.atmospheric_basis is not None: if atm_ip is None: @@ -118,11 +131,11 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con aip.connect_to_ground(gip) if params.T4: - agotensor = SymbolicTensorT4(params, aip, oip, gip) + agotensor = SymbolicQgsTensorT4(params, aip, oip, gip) elif params.dynamic_T: - agotensor = SymbolicTensorDynamicT(params, aip, oip, gip) + agotensor = SymbolicQgsTensorDynamicT(params, aip, oip, gip) else: - agotensor = SymbolicTensorLinear(params, aip, oip, gip) + agotensor = SymbolicQgsTensor(params, aip, oip, gip) xx = list() xx.append(1) @@ -133,12 +146,14 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con if params.dynamic_T: eq = symbolic_sparse_mult5(agotensor.sub_tensor(continuation_variables=continuation_variables), xx, xx, xx, xx) if return_jacobian: - dict_eq = symbolic_sparse_mult4(agotensor.sub_tensor(agotensor.jac_dic, continuation_variables=continuation_variables), xx, xx, xx) + dict_eq = symbolic_sparse_mult4(agotensor.sub_tensor( + agotensor.jac_dic, continuation_variables=continuation_variables), xx, xx, xx) else: eq = symbolic_sparse_mult3(agotensor.sub_tensor(continuation_variables=continuation_variables), xx, xx) if return_jacobian: - dict_eq = symbolic_sparse_mult2(agotensor.sub_tensor(agotensor.jac_dic, continuation_variables=continuation_variables), xx) + dict_eq = symbolic_sparse_mult2(agotensor.sub_tensor( + agotensor.jac_dic, continuation_variables=continuation_variables), xx) eq_simplified = dict() dict_eq_simplified = dict() @@ -158,7 +173,8 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con if return_jacobian: dict_eq_simplified = dict_eq - func = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, continuation_variables=continuation_variables) + func = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, + continuation_variables=continuation_variables) ret = list() ret.append('\n'.join(func)) @@ -178,8 +194,8 @@ def translate_equations(equations, language='python'): Parameters ---------- - equations: dict - Dictinary of the symbolic model equations + equations: dict(~sympy.core.expr.Expr) + Dictionary of the symbolic model equations. language: string Language syntax that the equations are returned in. Options are: - `python` @@ -191,7 +207,7 @@ def translate_equations(equations, language='python'): Returns ------- - str_eq: dict + str_eq: dict(str) Dictionary of strings of the model equations. """ @@ -223,18 +239,18 @@ def translate_equations(equations, language='python'): def format_equations(equations, params, save_loc=None, language='python', print_equations=False): - """Function formats the equations, in the programming language specified, and saves the equations to the specified location. - The variables in the equation are substituted if the model variable is input. + """Function formats the equations, in the programming language specified, and saves the equations to the specified + location. The variables in the equation are substituted if the model variable is input. Parameters ---------- - equations: dict - Dictionary of symbolic model equations + equations: dict(~sympy.core.expr.Expr) + Dictionary of symbolic model equations. params: QgParams - qgs model params - save_loc: String - location to save the outputs as a .txt file - language: string + The parameters fully specifying the model configuration. + save_loc: str + Location to save the outputs as a .txt file. + language: str Language syntax that the equations are returned in. Options are: - `python` - `fortran` @@ -243,15 +259,18 @@ def format_equations(equations, params, save_loc=None, language='python', print_ - `mathematica` Default to `python`. print_equations: bool - If `True`, equations are printed by the function, if `False`, equation string is returned by the function. Defaults to `False` + If `True`, equations are printed by the function, if `False`, equation strings are returned by the function. + Defaults to `False` Returns ------- - equation_dict: dict - Dictionary of symbolic model equations, that have been substituted with numerical values + equation_dict: dict(~sympy.core.expr.Expr) + Dictionary of symbolic model equations, that have been substituted with numerical values. free_vars: Set - Set of strings of model variables that have not been substituted in this function, and remain as variables in the equations. + Set of strings of model variables that have not been substituted in this function, and remain as variables in + the equations. + # Jonathan: I don't see where free_vars is returned in the code !! """ equation_dict = dict() @@ -298,17 +317,18 @@ def format_equations(equations, params, save_loc=None, language='python', print_ def equation_as_function(equations, params, string_output=True, language='python', continuation_variables=None): - """Converts the symbolic equations to a function in string format in the language syntax specified, or a lambdified python function + """Converts the symbolic equations to a function in string format in the language syntax specified, + or a lambdified python function. Parameters ---------- - equations: dict - Dictionary of the substituted symbolic model equations + equations: dict(~sympy.core.expr.Expr) + Dictionary of the substituted symbolic model equations. params: QgParams The parameters fully specifying the model configuration. string_output: bool - If `True`, returns a lambdified python function, if `False` returns a string function. Defaults to `True` - language: string + If `True`, returns a lambdified python function, if `False` returns a string function. Defaults to `True`. + language: str Language syntax that the equations are returned in. Options are: - `python` - `fortran` @@ -316,13 +336,14 @@ def equation_as_function(equations, params, string_output=True, language='python - `auto` - `mathematica` Default to `python`. - continuation_variables: Set or list or None - Variables that are not substituted with numerical values. If None, no symbols are substituted + continuation_variables: ~sympy.sets.sets.Set(~sympy.core.symbol.Symbol) or list(~sympy.core.symbol.Symbol) or None + Variables that are not substituted with numerical values. If `None`, no symbols are substituted. Returns ------- f_output: callable or str - If string_output is `True`, output is a function in the specified language syntax, if `False` the output is a lambdified python function + If string_output is `True`, output is a function in the specified language syntax, if `False` the output is + a lambdified python function. """ @@ -414,7 +435,7 @@ def equation_as_function(equations, params, string_output=True, language='python return f_output -def create_auto_file(equations, params, continuation_variables): +def create_auto_file(equations, params, continuation_variables, auto_main_template=None, auto_c_template=None): """Creates the auto configuration file and the model file. Saves files to specified folder. @@ -426,7 +447,12 @@ def create_auto_file(equations, params, continuation_variables): The parameters fully specifying the model configuration. continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) Variables that are not substituted with numerical values. If None, no symbols are substituted - + auto_main_template: str, optional + The template to be used to generate the main AUTO file. + If not provided, use the default template. + auto_c_template: str, optional + The template to be used to generate the AUTO config file. + If not provided, use the default template. """ # TODO: Find out best way to save these files @@ -436,12 +462,9 @@ def create_auto_file(equations, params, continuation_variables): # The existing model parameters are used to populate the auto file # The variables given as `continuation_variables` remain in the equations. # There is a limit of 1-10 remaining variables - base_path = os.path.dirname(__file__) - base_file = '.modelproto' - base_config = '.cproto' if (len(continuation_variables) < 1) or (len(continuation_variables) > 10): - ValueError("Too many variables for auto file") + raise ValueError("Too many variables for auto file") # Declare variables declare_var = list() @@ -463,13 +486,14 @@ def create_auto_file(equations, params, continuation_variables): # Writing model file ################ - # Open base file and input strings - f_base = open(base_path + '/' + base_file, 'r') - lines = f_base.readlines() - f_base.close() + if auto_main_template is not None: + lines = auto_main_template + else: + lines = default_auto_main_template auto_file = list() # TODO: Tabs not working here correctly + # Jonathan: Is it solved with the new method? for ln in lines: if 'PARAMETER DECLARATION' in ln: for dv in declare_var: @@ -490,9 +514,10 @@ def create_auto_file(equations, params, continuation_variables): # Writing config file ################ - c_base = open(base_path + '/' + base_config, 'r') - lines = c_base.readlines() - c_base.close() + if auto_c_template is not None: + lines = auto_c_template + else: + lines = default_auto_c_template auto_config = list() for ln in lines: @@ -570,3 +595,307 @@ def _variable_names(params): output[i+1] = v return output + + +# ------------- Default AUTO files templates ---------------- + +default_auto_main_template = """!---------------------------------------------------------------------- +!---------------------------------------------------------------------- +! AUTO file for qgs model +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + +SUBROUTINE FUNC(NDIM,U,ICP,PAR,IJAC,F,DFDU,DFDP) + !--------- ---- + + ! Evaluates the algebraic equations or ODE right hand side + + ! Input arguments : + ! NDIM : Dimension of the algebraic or ODE system + ! U : State variables + ! ICP : Array indicating the free parameter(s) + ! PAR : Equation parameters + + ! Values to be returned : + ! F : Equation or ODE right hand side values + + ! Normally unused Jacobian arguments : IJAC, DFDU, DFDP (see manual) + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM, IJAC, ICP(*) + DOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) + DOUBLE PRECISION, INTENT(OUT) :: F(NDIM) + DOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM,NDIM),DFDP(NDIM,*) + + + +! PARAMETER DECLERATION + +! CONTINUATION PARAMETERS + + +! EVOLUTION EQUATIONS + +END SUBROUTINE FUNC + +!----------------------------------------------------------------------- +!----------------------------------------------------------------------- + +SUBROUTINE STPNT(NDIM,U,PAR,T) + !--------- ----- + + ! Input arguments : + ! NDIM : Dimension of the algebraic or ODE system + + ! Values to be returned : + ! U : A starting solution vector + ! PAR : The corresponding equation-parameter values + + ! Note : For time- or space-dependent solutions this subroutine has + ! the scalar input parameter T contains the varying time or space + ! variable value. + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM + DOUBLE PRECISION, INTENT(INOUT) :: U(NDIM),PAR(*) + DOUBLE PRECISION, INTENT(IN) :: T + DOUBLE PRECISION :: X(NDIM+1) + INTEGER :: i,is + + ! Initialize the equation parameters + +! INITIALISE PARAMETERS + + ! Initialize the solution + U = 0.0d0 + ! Initialization from a solution file (selection with PAR36) + ! open(unit=15,file='',status='old') + ! is=int(PAR(36)) + ! if (is.gt.0) print*, 'Loading from solution :',is + ! DO i=1,is + ! read(15,*) X + ! ENDDO + ! close(15) + ! U=X(2:NDIM+1) + + +END SUBROUTINE STPNT + +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + +SUBROUTINE BCND(NDIM,PAR,ICP,NBC,U0,U1,FB,IJAC,DBC) + !--------- ---- + + ! Boundary Conditions + + ! Input arguments : + ! NDIM : Dimension of the ODE system + ! PAR : Equation parameters + ! ICP : Array indicating the free parameter(s) + ! NBC : Number of boundary conditions + ! U0 : State variable values at the left boundary + ! U1 : State variable values at the right boundary + + ! Values to be returned : + ! FB : The values of the boundary condition functions + + ! Normally unused Jacobian arguments : IJAC, DBC (see manual) + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM, ICP(*), NBC, IJAC + DOUBLE PRECISION, INTENT(IN) :: PAR(*), U0(NDIM), U1(NDIM) + DOUBLE PRECISION, INTENT(OUT) :: FB(NBC) + DOUBLE PRECISION, INTENT(INOUT) :: DBC(NBC,*) + + !X FB(1)= + !X FB(2)= + +END SUBROUTINE BCND + +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + +SUBROUTINE ICND(NDIM,PAR,ICP,NINT,U,UOLD,UDOT,UPOLD,FI,IJAC,DINT) + + ! Integral Conditions + + ! Input arguments : + ! NDIM : Dimension of the ODE system + ! PAR : Equation parameters + ! ICP : Array indicating the free parameter(s) + ! NINT : Number of integral conditions + ! U : Value of the vector function U at `time' t + + ! The following input arguments, which are normally not needed, + ! correspond to the preceding point on the solution branch + ! UOLD : The state vector at 'time' t + ! UDOT : Derivative of UOLD with respect to arclength + ! UPOLD : Derivative of UOLD with respect to `time' + + ! Normally unused Jacobian arguments : IJAC, DINT + + ! Values to be returned : + ! FI : The value of the vector integrand + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM, ICP(*), NINT, IJAC + DOUBLE PRECISION, INTENT(IN) :: PAR(*) + DOUBLE PRECISION, INTENT(IN) :: U(NDIM), UOLD(NDIM), UDOT(NDIM), UPOLD(NDIM) + DOUBLE PRECISION, INTENT(OUT) :: FI(NINT) + DOUBLE PRECISION, INTENT(INOUT) :: DINT(NINT,*) + +END SUBROUTINE ICND + +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + + +SUBROUTINE FOPT(NDIM,U,ICP,PAR,IJAC,FS,DFDU,DFDP) + !--------- ---- + ! + ! Defines the objective function for algebraic optimization problems + ! + ! Supplied variables : + ! NDIM : Dimension of the state equation + ! U : The state vector + ! ICP : Indices of the control parameters + ! PAR : The vector of control parameters + ! + ! Values to be returned : + ! FS : The value of the objective function + ! + ! Normally unused Jacobian argument : IJAC, DFDP + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM, ICP(*), IJAC + DOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) + DOUBLE PRECISION, INTENT(OUT) :: FS + DOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM),DFDP(*) + +END SUBROUTINE FOPT + +!---------------------------------------------------------------------- +!---------------------------------------------------------------------- + +SUBROUTINE PVLS(NDIM,U,PAR) + !--------- ---- + + IMPLICIT NONE + INTEGER, INTENT(IN) :: NDIM + DOUBLE PRECISION, INTENT(INOUT) :: U(NDIM) + DOUBLE PRECISION, INTENT(INOUT) :: PAR(*) + DOUBLE PRECISION :: GETP,pi,realfm,imagfm,imagfm1 + DOUBLE PRECISION :: lw,lw1 + LOGICAL, SAVE :: first = .TRUE. + DOUBLE PRECISION :: T + INTEGER :: i + + IF (first) THEN + CALL STPNT(NDIM,U,PAR,T) + first = .FALSE. + ENDIF + + !PAR(26)=U(44) + !PAR(27)=U(52) + PAR(25)=0. + pi = 4*ATAN(1d0) + i=1 + lw=100. + lw1=101. + DO WHILE(i < NDIM) + realfm = GETP('EIG',I*2-1,U) + IF (ABS(realfm) < lw) THEN + lw = ABS(realfm) + lw1 = ABS(GETP('EIG',(I+1)*2-1,U)) + imagfm1 = ABS(GETP('EIG',(I+1)*2,U)) + imagfm = ABS(GETP('EIG',I*2,U)) + END IF + i=i+1 + END DO + IF ((lw==lw1).AND.(imagfm1==imagfm).AND.(imagfm/=0.D0)) THEN + PAR(25) = 2*pi/imagfm + ENDIF + !---------------------------------------------------------------------- + ! NOTE : + ! Parameters set in this subroutine should be considered as ``solution + ! measures'' and be used for output purposes only. + ! + ! They should never be used as `true'' continuation parameters. + ! + ! They may, however, be added as ``over-specified parameters'' in the + ! parameter list associated with the AUTO-Constant NICP, in order to + ! print their values on the screen and in the ``p.xxx file. + ! + ! They may also appear in the list associated with AUTO-Constant NUZR. + ! + !---------------------------------------------------------------------- + ! For algebraic problems the argument U is, as usual, the state vector. + ! For differential equations the argument U represents the approximate + ! solution on the entire interval [0,1]. In this case its values must + ! be accessed indirectly by calls to GETP, as illustrated below. + !---------------------------------------------------------------------- + ! + ! Set PAR(2) equal to the L2-norm of U(1) + !X PAR(2)=GETP('NRM',1,U) + ! + ! Set PAR(3) equal to the minimum of U(2) + !X PAR(3)=GETP('MIN',2,U) + ! + ! Set PAR(4) equal to the value of U(2) at the left boundary. + !X PAR(4)=GETP('BV0',2,U) + ! + ! Set PAR(5) equal to the pseudo-arclength step size used. + !X PAR(5)=GETP('STP',1,U) + ! + !---------------------------------------------------------------------- + ! The first argument of GETP may be one of the following: + ! 'NRM' (L2-norm), 'MAX' (maximum), + ! 'INT' (integral), 'BV0 (left boundary value), + ! 'MIN' (minimum), 'BV1' (right boundary value). + ! + ! Also available are + ! 'STP' (Pseudo-arclength step size used). + ! 'FLD' (`Fold function', which vanishes at folds). + ! 'BIF' (`Bifurcation function', which vanishes at singular points). + ! 'HBF' (`Hopf function'; which vanishes at Hopf points). + ! 'SPB' ( Function which vanishes at secondary periodic bifurcations). + !---------------------------------------------------------------------- +END SUBROUTINE PVLS +""" + +default_auto_c_template = """#Configuration files + +#Parameters name +# ! PARAMETERS +#Variables name +# ! VARIABLES +#Dimension of the system +# ! DIMENSION +#Problem type (1 for FP, 2 for PO, -2 for time integration) +IPS = 1 +#Start solution label +IRS = 0 +#Continuation parameters (in order of use) +# ! CONTINUATION ORDER +#Number of mesh intervals +NTST= 100 +#Print and restart every NPR steps (0 to disable) +NPR= 0 +#Number of bifurcating branches to compute (negative number means continue only in one direction) +MXBF=0 +#Detection of Special Points +ISP=2 +#Maximum number of iteration in the Newton-Chord method +ITNW=7 +#Arc-length continuation parameters +DS = 0.00001, DSMIN= 1e-15, DSMAX= 1.0 +#Precision parameters (Typiq. EPSS = EPSL * 10^-2) +EPSL=1e-07, EPSU=1e-07, EPSS=1e-05 +#Number of parameter (don't change it) +NPAR = 36 +#User defined value where to save the solution +# ! SOLUTION SAVE +#Stop conditions +# ! STOP CONDITIONS +""" diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index a47acf7..a1860c5 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -19,7 +19,7 @@ # Jonathan: All public functions should have a docstring -class SymbolicTensorLinear(object): +class SymbolicQgsTensor(object): """Symbolic qgs tendencies tensor class. Parameters @@ -834,7 +834,7 @@ def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): print(str(ix) + ": " + str(output_val)) -class SymbolicTensorDynamicT(SymbolicTensorLinear): +class SymbolicQgsTensorDynamicT(SymbolicQgsTensor): """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. Parameters @@ -872,7 +872,7 @@ class SymbolicTensorDynamicT(SymbolicTensorLinear): def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): - SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) + SymbolicQgsTensor.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) if params.dynamic_T: self.compute_tensor() @@ -1179,7 +1179,7 @@ def compute_tensor(self): # TODO: Make a proper error message for here raise ValueError("Parameters are set for T4 version, set dynamic_T=True") - symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) + symbolic_dict_linear = SymbolicQgsTensor._compute_tensor_dicts(self) symbolic_dict_linear = _shift_dict_keys(symbolic_dict_linear, (0, 0)) symbolic_dict_dynT = self._compute_tensor_dicts() @@ -1191,7 +1191,7 @@ def compute_tensor(self): self._set_tensor(symbolic_dict_dynT) -class SymbolicTensorT4(SymbolicTensorLinear): +class SymbolicQgsTensorT4(SymbolicQgsTensor): # TODO: this takes a long time (>1hr) to run. I think we need a better way to run the non-stored z, v, Z, V IPs. Maybe do not allow `n` as a continuation parameter for this version? # Jonathan: Could be done by raising an error if so. """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. @@ -1231,7 +1231,7 @@ class SymbolicTensorT4(SymbolicTensorLinear): def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): - SymbolicTensorLinear.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) + SymbolicQgsTensor.__init__(self, params, atmospheric_inner_products, oceanic_inner_products, ground_inner_products) if params.T4: self.compute_tensor() @@ -1377,7 +1377,7 @@ def compute_tensor(self): if not self.params.T4: raise ValueError("Parameters are not set for T4 version") - symbolic_dict_linear = SymbolicTensorLinear._compute_tensor_dicts(self) + symbolic_dict_linear = SymbolicQgsTensor._compute_tensor_dicts(self) symbolic_dict_linear = _shift_dict_keys(symbolic_dict_linear, (0, 0)) symbolic_dict_T4 = self._compute_non_stored_full_dict() From 08e03c0ffbb439a397212e5b7a273672f7af4bac Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 18 Oct 2023 11:46:53 +0200 Subject: [PATCH 088/143] Deleting the hidden template files. Test are passing. --- qgs/functions/.cproto | 34 ----- qgs/functions/.modelproto | 263 -------------------------------------- 2 files changed, 297 deletions(-) delete mode 100644 qgs/functions/.cproto delete mode 100644 qgs/functions/.modelproto diff --git a/qgs/functions/.cproto b/qgs/functions/.cproto deleted file mode 100644 index 89561b7..0000000 --- a/qgs/functions/.cproto +++ /dev/null @@ -1,34 +0,0 @@ -#Configuration files - -#Parameters name -# ! PARAMETERS -#Variables name -# ! VARIABLES -#Dimension of the system -# ! DIMENSION -#Problem type (1 for FP, 2 for PO, -2 for time integration) -IPS = 1 -#Start solution label -IRS = 0 -#Continuation parameters (in order of use) -# ! CONTINUATION ORDER -#Number of mesh intervals -NTST= 100 -#Print and restart every NPR steps (0 to disable) -NPR= 0 -#Number of bifurcating branches to compute (negative number means continue only in one direction) -MXBF=0 -#Detection of Special Points -ISP=2 -#Maximum number of iteration in the Newton-Chord method -ITNW=7 -#Arc-length continuation parameters -DS = 0.00001, DSMIN= 1e-15, DSMAX= 1.0 -#Precision parameters (Typiq. EPSS = EPSL * 10^-2) -EPSL=1e-07, EPSU=1e-07, EPSS=1e-05 -#Number of parameter (don't change it) -NPAR = 36 -#User defined value where to save the solution -# ! SOLUTION SAVE -#Stop conditions -# ! STOP CONDITIONS diff --git a/qgs/functions/.modelproto b/qgs/functions/.modelproto deleted file mode 100644 index 61cabc2..0000000 --- a/qgs/functions/.modelproto +++ /dev/null @@ -1,263 +0,0 @@ -!---------------------------------------------------------------------- -!---------------------------------------------------------------------- -! AUTO file for qgs model -!---------------------------------------------------------------------- -!---------------------------------------------------------------------- - -SUBROUTINE FUNC(NDIM,U,ICP,PAR,IJAC,F,DFDU,DFDP) - !--------- ---- - - ! Evaluates the algebraic equations or ODE right hand side - - ! Input arguments : - ! NDIM : Dimension of the algebraic or ODE system - ! U : State variables - ! ICP : Array indicating the free parameter(s) - ! PAR : Equation parameters - - ! Values to be returned : - ! F : Equation or ODE right hand side values - - ! Normally unused Jacobian arguments : IJAC, DFDU, DFDP (see manual) - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM, IJAC, ICP(*) - DOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) - DOUBLE PRECISION, INTENT(OUT) :: F(NDIM) - DOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM,NDIM),DFDP(NDIM,*) - - - -! PARAMETER DECLERATION - -! CONTINUATION PARAMETERS - - -! EVOLUTION EQUATIONS - -END SUBROUTINE FUNC - -!----------------------------------------------------------------------- -!----------------------------------------------------------------------- - -SUBROUTINE STPNT(NDIM,U,PAR,T) - !--------- ----- - - ! Input arguments : - ! NDIM : Dimension of the algebraic or ODE system - - ! Values to be returned : - ! U : A starting solution vector - ! PAR : The corresponding equation-parameter values - - ! Note : For time- or space-dependent solutions this subroutine has - ! the scalar input parameter T contains the varying time or space - ! variable value. - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM - DOUBLE PRECISION, INTENT(INOUT) :: U(NDIM),PAR(*) - DOUBLE PRECISION, INTENT(IN) :: T - DOUBLE PRECISION :: X(NDIM+1) - INTEGER :: i,is - - ! Initialize the equation parameters - -! INITIALISE PARAMETERS - - ! Initialize the solution - U = 0.0d0 - ! Initialization from a solution file (selection with PAR36) - ! open(unit=15,file='',status='old') - ! is=int(PAR(36)) - ! if (is.gt.0) print*, 'Loading from solution :',is - ! DO i=1,is - ! read(15,*) X - ! ENDDO - ! close(15) - ! U=X(2:NDIM+1) - - -END SUBROUTINE STPNT - -!---------------------------------------------------------------------- -!---------------------------------------------------------------------- - -SUBROUTINE BCND(NDIM,PAR,ICP,NBC,U0,U1,FB,IJAC,DBC) - !--------- ---- - - ! Boundary Conditions - - ! Input arguments : - ! NDIM : Dimension of the ODE system - ! PAR : Equation parameters - ! ICP : Array indicating the free parameter(s) - ! NBC : Number of boundary conditions - ! U0 : State variable values at the left boundary - ! U1 : State variable values at the right boundary - - ! Values to be returned : - ! FB : The values of the boundary condition functions - - ! Normally unused Jacobian arguments : IJAC, DBC (see manual) - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM, ICP(*), NBC, IJAC - DOUBLE PRECISION, INTENT(IN) :: PAR(*), U0(NDIM), U1(NDIM) - DOUBLE PRECISION, INTENT(OUT) :: FB(NBC) - DOUBLE PRECISION, INTENT(INOUT) :: DBC(NBC,*) - - !X FB(1)= - !X FB(2)= - -END SUBROUTINE BCND - -!---------------------------------------------------------------------- -!---------------------------------------------------------------------- - -SUBROUTINE ICND(NDIM,PAR,ICP,NINT,U,UOLD,UDOT,UPOLD,FI,IJAC,DINT) - - ! Integral Conditions - - ! Input arguments : - ! NDIM : Dimension of the ODE system - ! PAR : Equation parameters - ! ICP : Array indicating the free parameter(s) - ! NINT : Number of integral conditions - ! U : Value of the vector function U at `time' t - - ! The following input arguments, which are normally not needed, - ! correspond to the preceding point on the solution branch - ! UOLD : The state vector at 'time' t - ! UDOT : Derivative of UOLD with respect to arclength - ! UPOLD : Derivative of UOLD with respect to `time' - - ! Normally unused Jacobian arguments : IJAC, DINT - - ! Values to be returned : - ! FI : The value of the vector integrand - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM, ICP(*), NINT, IJAC - DOUBLE PRECISION, INTENT(IN) :: PAR(*) - DOUBLE PRECISION, INTENT(IN) :: U(NDIM), UOLD(NDIM), UDOT(NDIM), UPOLD(NDIM) - DOUBLE PRECISION, INTENT(OUT) :: FI(NINT) - DOUBLE PRECISION, INTENT(INOUT) :: DINT(NINT,*) - -END SUBROUTINE ICND - -!---------------------------------------------------------------------- -!---------------------------------------------------------------------- - - -SUBROUTINE FOPT(NDIM,U,ICP,PAR,IJAC,FS,DFDU,DFDP) - !--------- ---- - ! - ! Defines the objective function for algebraic optimization problems - ! - ! Supplied variables : - ! NDIM : Dimension of the state equation - ! U : The state vector - ! ICP : Indices of the control parameters - ! PAR : The vector of control parameters - ! - ! Values to be returned : - ! FS : The value of the objective function - ! - ! Normally unused Jacobian argument : IJAC, DFDP - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM, ICP(*), IJAC - DOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) - DOUBLE PRECISION, INTENT(OUT) :: FS - DOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM),DFDP(*) - -END SUBROUTINE FOPT - -!---------------------------------------------------------------------- -!---------------------------------------------------------------------- - -SUBROUTINE PVLS(NDIM,U,PAR) - !--------- ---- - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM - DOUBLE PRECISION, INTENT(INOUT) :: U(NDIM) - DOUBLE PRECISION, INTENT(INOUT) :: PAR(*) - DOUBLE PRECISION :: GETP,pi,realfm,imagfm,imagfm1 - DOUBLE PRECISION :: lw,lw1 - LOGICAL, SAVE :: first = .TRUE. - DOUBLE PRECISION :: T - INTEGER :: i - - IF (first) THEN - CALL STPNT(NDIM,U,PAR,T) - first = .FALSE. - ENDIF - - !PAR(26)=U(44) - !PAR(27)=U(52) - PAR(25)=0. - pi = 4*ATAN(1d0) - i=1 - lw=100. - lw1=101. - DO WHILE(i < NDIM) - realfm = GETP('EIG',I*2-1,U) - IF (ABS(realfm) < lw) THEN - lw = ABS(realfm) - lw1 = ABS(GETP('EIG',(I+1)*2-1,U)) - imagfm1 = ABS(GETP('EIG',(I+1)*2,U)) - imagfm = ABS(GETP('EIG',I*2,U)) - END IF - i=i+1 - END DO - IF ((lw==lw1).AND.(imagfm1==imagfm).AND.(imagfm/=0.D0)) THEN - PAR(25) = 2*pi/imagfm - ENDIF - !---------------------------------------------------------------------- - ! NOTE : - ! Parameters set in this subroutine should be considered as ``solution - ! measures'' and be used for output purposes only. - ! - ! They should never be used as `true'' continuation parameters. - ! - ! They may, however, be added as ``over-specified parameters'' in the - ! parameter list associated with the AUTO-Constant NICP, in order to - ! print their values on the screen and in the ``p.xxx file. - ! - ! They may also appear in the list associated with AUTO-Constant NUZR. - ! - !---------------------------------------------------------------------- - ! For algebraic problems the argument U is, as usual, the state vector. - ! For differential equations the argument U represents the approximate - ! solution on the entire interval [0,1]. In this case its values must - ! be accessed indirectly by calls to GETP, as illustrated below. - !---------------------------------------------------------------------- - ! - ! Set PAR(2) equal to the L2-norm of U(1) - !X PAR(2)=GETP('NRM',1,U) - ! - ! Set PAR(3) equal to the minimum of U(2) - !X PAR(3)=GETP('MIN',2,U) - ! - ! Set PAR(4) equal to the value of U(2) at the left boundary. - !X PAR(4)=GETP('BV0',2,U) - ! - ! Set PAR(5) equal to the pseudo-arclength step size used. - !X PAR(5)=GETP('STP',1,U) - ! - !---------------------------------------------------------------------- - ! The first argument of GETP may be one of the following: - ! 'NRM' (L2-norm), 'MAX' (maximum), - ! 'INT' (integral), 'BV0 (left boundary value), - ! 'MIN' (minimum), 'BV1' (right boundary value). - ! - ! Also available are - ! 'STP' (Pseudo-arclength step size used). - ! 'FLD' (`Fold function', which vanishes at folds). - ! 'BIF' (`Bifurcation function', which vanishes at singular points). - ! 'HBF' (`Hopf function'; which vanishes at Hopf points). - ! 'SPB' ( Function which vanishes at secondary periodic bifurcations). - !---------------------------------------------------------------------- -END SUBROUTINE PVLS \ No newline at end of file From 31ca66cdd19b06d670e8162dfc76b7415534e6de Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 18 Oct 2023 17:21:31 +0200 Subject: [PATCH 089/143] Continued comments --- qgs/functions/symbolic_mul.py | 8 +++---- qgs/tensors/symbolic_qgtensor.py | 36 +++++++++++++++++++------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py index a2cfc04..60d93f3 100644 --- a/qgs/functions/symbolic_mul.py +++ b/qgs/functions/symbolic_mul.py @@ -71,7 +71,7 @@ def symbolic_sparse_mult2(dic, vec_a): Returns ------- - res: dict # Jonathan: dict of what ? + res: dict(~sympy.core.symbol.Symbol) The matrix :math:`A_{i,j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. """ @@ -104,7 +104,7 @@ def symbolic_sparse_mult3(dic, vec_a, vec_b): Returns ------- - res: list(~sympy.core.symbol.Symbol) + res: dict(~sympy.core.symbol.Symbol) The vector :math:`v_i`, of shape (:attr:`~.params.QgParams.ndim` + 1,), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. @@ -141,7 +141,7 @@ def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): Returns ------- - res: list(~sympy.core.symbol.Symbol) + res: dict(~sympy.core.symbol.Symbol) The matrix :math:`A_{i, j}`, of shape (:attr:`~.params.QgParams.ndim` + 1, :attr:`~.params.QgParams.ndim` + 1), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. """ @@ -179,7 +179,7 @@ def symbolic_sparse_mult5(dic, vec_a, vec_b, vec_c, vec_d): Returns ------- - res: list(~sympy.core.symbol.Symbol) + res: dict(~sympy.core.symbol.Symbol) The vector :math:`v_i`, of shape (:attr:`~.params.QgParams.ndim` + 1,), contained in a dictionary, where the keys are the tensor coordinates, and the values are the tensor values. """ diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index a1860c5..a09a80c 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -1,15 +1,16 @@ """ symbolic qgs tensor module - ================= + ========================== - This module computes and holds the symbolic representation of the tensors representing the tendencies of the model's equations. + This module computes and holds the symbolic representation of the tensors representing the tendencies of the model's + equations. """ from qgs.functions.symbolic_mul import add_to_dict, symbolic_tensordot from qgs.params.params import Parameter, ScalingParameter, ParametersArray import numpy as np -import sympy as sy +from sympy import simplify import pickle from sympy.matrices.immutable import ImmutableSparseMatrix @@ -27,7 +28,8 @@ class SymbolicQgsTensor(object): params: None or QgParams, optional The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional - The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. + The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are + projected. If None, disable the atmospheric tendencies. Default to `None`. The inner product is returned in symbolic or numeric form. oceanic_inner_products: None or OceanicInnerProducts, optional @@ -44,7 +46,8 @@ class SymbolicQgsTensor(object): params: None or QgParams The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts - The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. + The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are + projected. If None, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. @@ -58,7 +61,8 @@ class SymbolicQgsTensor(object): The jacobian tensor :math:`\\mathcal{T}_{i,j,k} + \\mathcal{T}_{i,k,j}` :math:`i`-th components. """ - def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, ground_inner_products=None): + def __init__(self, params=None, atmospheric_inner_products=None, oceanic_inner_products=None, + ground_inner_products=None): self.atmospheric_inner_products = atmospheric_inner_products self.oceanic_inner_products = oceanic_inner_products @@ -215,7 +219,8 @@ def _compute_tensor_dicts(self): M_psio = dict() for i in range(offset, nvar[3]): for j in range(offset, nvar[3]): - M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.params.G.symbolic_expression * bips.U(i, j) + M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.params.G.symbolic_expression \ + * bips.U(i, j) M_psio = ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) M_psio = M_psio.inverse() @@ -697,8 +702,8 @@ def _set_symbolic_tensor(self): jacobian_tensor = ImmutableSparseNDimArray(self.jac_dic.copy(), dims) tensor = ImmutableSparseNDimArray(self.tensor_dic.copy(), dims) - self.jacobian_tensor = jacobian_tensor.applyfunc(sy.simplify) - self.tensor = tensor.applyfunc(sy.simplify) + self.jacobian_tensor = jacobian_tensor.applyfunc(simplify) + self.tensor = tensor.applyfunc(simplify) @staticmethod def remove_dic_zeros(dic): @@ -754,7 +759,7 @@ def save_to_file(self, filename, **kwargs): pickle.dump(self.__dict__, f, **kwargs) f.close() - def sub_tensor(self, tensor=None, continuation_variables=list()): + def sub_tensor(self, tensor=None, continuation_variables=None): """Uses sympy substitution to convert the symbolic tensor or a symbolic dictionary to a numerical one. Parameters @@ -770,6 +775,9 @@ def sub_tensor(self, tensor=None, continuation_variables=list()): Dictionary of the substituted tensor of the model tendencies, with coordinates and value """ + if continuation_variables is None: + continuation_variables = list() + param_subs = _parameter_substitutions(self.params, continuation_variables) if tensor is None: @@ -1193,7 +1201,7 @@ def compute_tensor(self): class SymbolicQgsTensorT4(SymbolicQgsTensor): # TODO: this takes a long time (>1hr) to run. I think we need a better way to run the non-stored z, v, Z, V IPs. Maybe do not allow `n` as a continuation parameter for this version? - # Jonathan: Could be done by raising an error if so. + # Jonathan: Could be done by raising a warning if so. """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. Parameters @@ -1417,15 +1425,13 @@ def _shift_dict_keys(dic, shift): return shifted_dic -def _parameter_substitutions(params, continuation_varaibles): +def _parameter_substitutions(params, continuation_variables): subs = _parameter_values(params) if 'scale_params' in params.__dict__.keys(): subs.update(_parameter_values(params.scale_params)) - # TODO: Is there a better way to do this which is dynamic, the __dict__ method doesnt include the properties? - # Jonathan: I don't understand # Manually add properties from class subs[params.scale_params.L.symbol] = params.scale_params.L subs[params.scale_params.beta.symbol] = params.scale_params.beta @@ -1451,7 +1457,7 @@ def _parameter_substitutions(params, continuation_varaibles): subs.update(_parameter_values(params.gotemperature_params)) # Remove variables in continuation variables - for cv in continuation_varaibles: + for cv in continuation_variables: if isinstance(cv, ParametersArray): for cv_i in cv.symbols: subs.pop(cv_i) From 1871e4df3e025026d48396cff0740324bfa9d38f Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 25 Oct 2023 15:07:17 +0200 Subject: [PATCH 090/143] Documentation added and fix of auto tab spacing --- notebooks/Symbolic Output-Linear.ipynb | 44 +-- qgs/functions/symbolic_output.py | 467 ++++++++++++------------- qgs/tensors/symbolic_qgtensor.py | 46 ++- 3 files changed, 281 insertions(+), 276 deletions(-) diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 478536f..9995430 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -58,18 +58,7 @@ "metadata": {}, "outputs": [], "source": [ - "model_parameters = QgParams(dynamic_T=False)\n", - "# model_parameters.set_params({'rr':100, 'n': 1.8})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "35037d2e", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.rr" + "model_parameters = QgParams(dynamic_T=False)" ] }, { @@ -94,8 +83,7 @@ "source": [ "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT\n", - "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" + "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT" ] }, { @@ -119,16 +107,6 @@ "from sympy.tensor.array import ImmutableDenseNDimArray" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "21592dd9", - "metadata": {}, - "outputs": [], - "source": [ - "str(model_parameters.atemperature_params.eps.symbol)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -149,16 +127,6 @@ "## Outputting model equations" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "311a854d", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, _parameter_values" - ] - }, { "cell_type": "code", "execution_count": null, @@ -166,7 +134,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations" + "from qgs.functions.symbolic_output import create_symbolic_equations, create_auto_file, default_auto_c_template" ] }, { @@ -187,7 +155,9 @@ "outputs": [], "source": [ "%%time\n", - "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.scale_params.n], language='auto', return_symbolic_qgtensor=True)" + "funcs, = create_symbolic_equations(model_parameters, continuation_variables=\n", + " {model_parameters.atemperature_params.eps},\n", + " language='python')" ] }, { @@ -195,7 +165,7 @@ "execution_count": null, "id": "5fd8283d", "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index f759ceb..97c5029 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -177,7 +177,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con continuation_variables=continuation_variables) ret = list() - ret.append('\n'.join(func)) + ret.append(func) if return_jacobian: ret.append(dict_eq_simplified) if return_inner_products: @@ -266,11 +266,6 @@ def format_equations(equations, params, save_loc=None, language='python', print_ ------- equation_dict: dict(~sympy.core.expr.Expr) Dictionary of symbolic model equations, that have been substituted with numerical values. - - free_vars: Set - Set of strings of model variables that have not been substituted in this function, and remain as variables in - the equations. - # Jonathan: I don't see where free_vars is returned in the code !! """ equation_dict = dict() @@ -364,6 +359,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('\tF['+str(n)+'] = ' + str(eq)) f_output.append('\treturn F') + f_output = '\n'.join(f_output) else: # Return a lambdified function vec = [Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] @@ -388,6 +384,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('\tdu['+str(n+1)+'] = ' + str(eq)) f_output.append('end') + f_output = '\n'.join(f_output) if language == 'fortran': eq_dict = translate_equations(eq_dict, language='fortran') @@ -413,12 +410,13 @@ def equation_as_function(equations, params, string_output=True, language='python f_output = _split_equations(eq_dict, f_output) f_output.append('END SUBROUTINE') + f_output = '\n'.join(f_output) if language == 'auto': eq_dict = translate_equations(eq_dict, language='fortran') eq_dict = _split_equations(eq_dict, f_output) - create_auto_file(eq_dict, params, continuation_variables) + f_output = create_auto_file(eq_dict, params, continuation_variables) if language == 'mathematica': # TODO: This function needs testing before release @@ -430,6 +428,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('F['+str(n+1)+'] = ' + str(eq)) # TODO !!!! Killing output as I have not tested the above code !!!! + f_output = '\n'.join(f_output) f_output = None return f_output @@ -453,9 +452,16 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa auto_c_template: str, optional The template to be used to generate the AUTO config file. If not provided, use the default template. + + Returns + ------- + auto_file: Str + The auto model file as a string + + auto_config: Str + Auto configuration file as a string """ - # TODO: Find out best way to save these files # TODO: There is some weird double tab spacings in the output, and I am not sure why # User passes the equations, with the variables to leave as variables. @@ -487,13 +493,11 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa # Writing model file ################ if auto_main_template is not None: - lines = auto_main_template + lines = auto_main_template.split('\n') else: - lines = default_auto_main_template + lines = default_auto_main_template.split('\n') auto_file = list() - # TODO: Tabs not working here correctly - # Jonathan: Is it solved with the new method? for ln in lines: if 'PARAMETER DECLARATION' in ln: for dv in declare_var: @@ -503,21 +507,19 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa auto_file.append('\t' + v) elif 'EVOLUTION EQUATIONS' in ln: for e in equations: - auto_file.append('\t' + e) + auto_file.append(e) elif 'INITIALISE PARAMETERS' in ln: for iv in var_ini: auto_file.append('\t' + iv) else: auto_file.append(ln.replace('\n', '')) - - print('\n'.join(auto_file)) # Writing config file ################ if auto_c_template is not None: - lines = auto_c_template + lines = auto_c_template.split('\n') else: - lines = default_auto_c_template + lines = default_auto_c_template.split('\n') auto_config = list() for ln in lines: @@ -544,9 +546,7 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa else: auto_config.append(ln.replace('\n', '')) - print('\n'.join(auto_config)) - - return equations + return '\n'.join(auto_file), '\n'.join(auto_config) def _split_equations(eq_dict, f_output, line_len=80): @@ -606,35 +606,33 @@ def _variable_names(params): !---------------------------------------------------------------------- SUBROUTINE FUNC(NDIM,U,ICP,PAR,IJAC,F,DFDU,DFDP) - !--------- ---- - - ! Evaluates the algebraic equations or ODE right hand side - - ! Input arguments : - ! NDIM : Dimension of the algebraic or ODE system - ! U : State variables - ! ICP : Array indicating the free parameter(s) - ! PAR : Equation parameters - - ! Values to be returned : - ! F : Equation or ODE right hand side values - - ! Normally unused Jacobian arguments : IJAC, DFDU, DFDP (see manual) - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM, IJAC, ICP(*) - DOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) - DOUBLE PRECISION, INTENT(OUT) :: F(NDIM) - DOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM,NDIM),DFDP(NDIM,*) - - - -! PARAMETER DECLERATION +\t!--------- ---- + +\t! Evaluates the algebraic equations or ODE right hand side + +\t! Input arguments : +\t! NDIM : Dimension of the algebraic or ODE system +\t! U : State variables +\t! ICP : Array indicating the free parameter(s) +\t! PAR : Equation parameters + +\t! Values to be returned : +\t! F : Equation or ODE right hand side values -! CONTINUATION PARAMETERS +\t! Normally unused Jacobian arguments : IJAC, DFDU, DFDP (see manual) + +\tIMPLICIT NONE +\tINTEGER, INTENT(IN) :: NDIM, IJAC, ICP(*) +\tDOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) +\tDOUBLE PRECISION, INTENT(OUT) :: F(NDIM) +\tDOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM,NDIM),DFDP(NDIM,*) + +! PARAMETER DECLARATION +! CONTINUATION PARAMETERS -! EVOLUTION EQUATIONS + +! EVOLUTION EQUATIONS END SUBROUTINE FUNC @@ -642,225 +640,224 @@ def _variable_names(params): !----------------------------------------------------------------------- SUBROUTINE STPNT(NDIM,U,PAR,T) - !--------- ----- - - ! Input arguments : - ! NDIM : Dimension of the algebraic or ODE system - - ! Values to be returned : - ! U : A starting solution vector - ! PAR : The corresponding equation-parameter values - - ! Note : For time- or space-dependent solutions this subroutine has - ! the scalar input parameter T contains the varying time or space - ! variable value. - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM - DOUBLE PRECISION, INTENT(INOUT) :: U(NDIM),PAR(*) - DOUBLE PRECISION, INTENT(IN) :: T - DOUBLE PRECISION :: X(NDIM+1) - INTEGER :: i,is - - ! Initialize the equation parameters +\t!--------- ----- + +\t! Input arguments : +\t! NDIM : Dimension of the algebraic or ODE system + +\t! Values to be returned : +\t! U : A starting solution vector +\t! PAR : The corresponding equation-parameter values + +\t! Note : For time- or space-dependent solutions this subroutine has +\t! the scalar input parameter T contains the varying time or space +\t! variable value. + +\tIMPLICIT NONE +\tINTEGER, INTENT(IN) :: NDIM +\tDOUBLE PRECISION, INTENT(INOUT) :: U(NDIM),PAR(*) +\tDOUBLE PRECISION, INTENT(IN) :: T +\tDOUBLE PRECISION :: X(NDIM+1) +\tINTEGER :: i,is + +\t! Initialize the equation parameters ! INITIALISE PARAMETERS - ! Initialize the solution - U = 0.0d0 - ! Initialization from a solution file (selection with PAR36) - ! open(unit=15,file='',status='old') - ! is=int(PAR(36)) - ! if (is.gt.0) print*, 'Loading from solution :',is - ! DO i=1,is - ! read(15,*) X - ! ENDDO - ! close(15) - ! U=X(2:NDIM+1) - - +\t! Initialize the solution +\tU = 0.0d0 +\t! Initialization from a solution file (selection with PAR36) +\t! open(unit=15,file='',status='old') +\t! is=int(PAR(36)) +\t! if (is.gt.0) print*, 'Loading from solution :',is +\t! DO i=1,is +\t! read(15,*) X +\t! ENDDO +\t! close(15) +\t! U=X(2:NDIM+1) + END SUBROUTINE STPNT - + !---------------------------------------------------------------------- !---------------------------------------------------------------------- SUBROUTINE BCND(NDIM,PAR,ICP,NBC,U0,U1,FB,IJAC,DBC) - !--------- ---- - - ! Boundary Conditions - - ! Input arguments : - ! NDIM : Dimension of the ODE system - ! PAR : Equation parameters - ! ICP : Array indicating the free parameter(s) - ! NBC : Number of boundary conditions - ! U0 : State variable values at the left boundary - ! U1 : State variable values at the right boundary - - ! Values to be returned : - ! FB : The values of the boundary condition functions - - ! Normally unused Jacobian arguments : IJAC, DBC (see manual) - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM, ICP(*), NBC, IJAC - DOUBLE PRECISION, INTENT(IN) :: PAR(*), U0(NDIM), U1(NDIM) - DOUBLE PRECISION, INTENT(OUT) :: FB(NBC) - DOUBLE PRECISION, INTENT(INOUT) :: DBC(NBC,*) - - !X FB(1)= - !X FB(2)= +\t!--------- ---- + +\t! Boundary Conditions + +\t! Input arguments : +\t! NDIM : Dimension of the ODE system +\t! PAR : Equation parameters +\t! ICP : Array indicating the free parameter(s) +\t! NBC : Number of boundary conditions +\t! U0 : State variable values at the left boundary +\t! U1 : State variable values at the right boundary +\t! Values to be returned : +\t! FB : The values of the boundary condition functions + +\t! Normally unused Jacobian arguments : IJAC, DBC (see manual) + +\tIMPLICIT NONE +\tINTEGER, INTENT(IN) :: NDIM, ICP(*), NBC, IJAC +\tDOUBLE PRECISION, INTENT(IN) :: PAR(*), U0(NDIM), U1(NDIM) +\tDOUBLE PRECISION, INTENT(OUT) :: FB(NBC) +\tDOUBLE PRECISION, INTENT(INOUT) :: DBC(NBC,*) + +\t!X FB(1)= +\t!X FB(2)= + END SUBROUTINE BCND - + !---------------------------------------------------------------------- !---------------------------------------------------------------------- - + SUBROUTINE ICND(NDIM,PAR,ICP,NINT,U,UOLD,UDOT,UPOLD,FI,IJAC,DINT) - ! Integral Conditions +\t! Integral Conditions - ! Input arguments : - ! NDIM : Dimension of the ODE system - ! PAR : Equation parameters - ! ICP : Array indicating the free parameter(s) - ! NINT : Number of integral conditions - ! U : Value of the vector function U at `time' t +\t! Input arguments : +\t! NDIM : Dimension of the ODE system +\t! PAR : Equation parameters +\t! ICP : Array indicating the free parameter(s) +\t! NINT : Number of integral conditions +\t! U : Value of the vector function U at `time' t - ! The following input arguments, which are normally not needed, - ! correspond to the preceding point on the solution branch - ! UOLD : The state vector at 'time' t - ! UDOT : Derivative of UOLD with respect to arclength - ! UPOLD : Derivative of UOLD with respect to `time' +\t! The following input arguments, which are normally not needed, +\t! correspond to the preceding point on the solution branch +\t! UOLD : The state vector at 'time' t +\t! UDOT : Derivative of UOLD with respect to arclength +\t! UPOLD : Derivative of UOLD with respect to `time' - ! Normally unused Jacobian arguments : IJAC, DINT +\t! Normally unused Jacobian arguments : IJAC, DINT - ! Values to be returned : - ! FI : The value of the vector integrand +\t! Values to be returned : +\t! FI : The value of the vector integrand - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM, ICP(*), NINT, IJAC - DOUBLE PRECISION, INTENT(IN) :: PAR(*) - DOUBLE PRECISION, INTENT(IN) :: U(NDIM), UOLD(NDIM), UDOT(NDIM), UPOLD(NDIM) - DOUBLE PRECISION, INTENT(OUT) :: FI(NINT) - DOUBLE PRECISION, INTENT(INOUT) :: DINT(NINT,*) +\tIMPLICIT NONE +\tINTEGER, INTENT(IN) :: NDIM, ICP(*), NINT, IJAC +\tDOUBLE PRECISION, INTENT(IN) :: PAR(*) +\tDOUBLE PRECISION, INTENT(IN) :: U(NDIM), UOLD(NDIM), UDOT(NDIM), UPOLD(NDIM) +\tDOUBLE PRECISION, INTENT(OUT) :: FI(NINT) +\tDOUBLE PRECISION, INTENT(INOUT) :: DINT(NINT,*) END SUBROUTINE ICND !---------------------------------------------------------------------- !---------------------------------------------------------------------- - + SUBROUTINE FOPT(NDIM,U,ICP,PAR,IJAC,FS,DFDU,DFDP) - !--------- ---- - ! - ! Defines the objective function for algebraic optimization problems - ! - ! Supplied variables : - ! NDIM : Dimension of the state equation - ! U : The state vector - ! ICP : Indices of the control parameters - ! PAR : The vector of control parameters - ! - ! Values to be returned : - ! FS : The value of the objective function - ! - ! Normally unused Jacobian argument : IJAC, DFDP - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM, ICP(*), IJAC - DOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) - DOUBLE PRECISION, INTENT(OUT) :: FS - DOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM),DFDP(*) - +\t!--------- ---- +\t! +\t! Defines the objective function for algebraic optimization problems +\t! +\t! Supplied variables : +\t! NDIM : Dimension of the state equation +\t! U : The state vector +\t! ICP : Indices of the control parameters +\t! PAR : The vector of control parameters +\t! +\t! Values to be returned : +\t! FS : The value of the objective function +\t! +\t! Normally unused Jacobian argument : IJAC, DFDP + +\tIMPLICIT NONE +\tINTEGER, INTENT(IN) :: NDIM, ICP(*), IJAC +\tDOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*) +\tDOUBLE PRECISION, INTENT(OUT) :: FS +\tDOUBLE PRECISION, INTENT(INOUT) :: DFDU(NDIM),DFDP(*) + END SUBROUTINE FOPT - + !---------------------------------------------------------------------- !---------------------------------------------------------------------- - + SUBROUTINE PVLS(NDIM,U,PAR) - !--------- ---- - - IMPLICIT NONE - INTEGER, INTENT(IN) :: NDIM - DOUBLE PRECISION, INTENT(INOUT) :: U(NDIM) - DOUBLE PRECISION, INTENT(INOUT) :: PAR(*) - DOUBLE PRECISION :: GETP,pi,realfm,imagfm,imagfm1 - DOUBLE PRECISION :: lw,lw1 - LOGICAL, SAVE :: first = .TRUE. - DOUBLE PRECISION :: T - INTEGER :: i - - IF (first) THEN - CALL STPNT(NDIM,U,PAR,T) - first = .FALSE. - ENDIF - - !PAR(26)=U(44) - !PAR(27)=U(52) - PAR(25)=0. - pi = 4*ATAN(1d0) - i=1 - lw=100. - lw1=101. - DO WHILE(i < NDIM) - realfm = GETP('EIG',I*2-1,U) - IF (ABS(realfm) < lw) THEN - lw = ABS(realfm) - lw1 = ABS(GETP('EIG',(I+1)*2-1,U)) - imagfm1 = ABS(GETP('EIG',(I+1)*2,U)) - imagfm = ABS(GETP('EIG',I*2,U)) - END IF - i=i+1 - END DO - IF ((lw==lw1).AND.(imagfm1==imagfm).AND.(imagfm/=0.D0)) THEN - PAR(25) = 2*pi/imagfm - ENDIF - !---------------------------------------------------------------------- - ! NOTE : - ! Parameters set in this subroutine should be considered as ``solution - ! measures'' and be used for output purposes only. - ! - ! They should never be used as `true'' continuation parameters. - ! - ! They may, however, be added as ``over-specified parameters'' in the - ! parameter list associated with the AUTO-Constant NICP, in order to - ! print their values on the screen and in the ``p.xxx file. - ! - ! They may also appear in the list associated with AUTO-Constant NUZR. - ! - !---------------------------------------------------------------------- - ! For algebraic problems the argument U is, as usual, the state vector. - ! For differential equations the argument U represents the approximate - ! solution on the entire interval [0,1]. In this case its values must - ! be accessed indirectly by calls to GETP, as illustrated below. - !---------------------------------------------------------------------- - ! - ! Set PAR(2) equal to the L2-norm of U(1) - !X PAR(2)=GETP('NRM',1,U) - ! - ! Set PAR(3) equal to the minimum of U(2) - !X PAR(3)=GETP('MIN',2,U) - ! - ! Set PAR(4) equal to the value of U(2) at the left boundary. - !X PAR(4)=GETP('BV0',2,U) - ! - ! Set PAR(5) equal to the pseudo-arclength step size used. - !X PAR(5)=GETP('STP',1,U) - ! - !---------------------------------------------------------------------- - ! The first argument of GETP may be one of the following: - ! 'NRM' (L2-norm), 'MAX' (maximum), - ! 'INT' (integral), 'BV0 (left boundary value), - ! 'MIN' (minimum), 'BV1' (right boundary value). - ! - ! Also available are - ! 'STP' (Pseudo-arclength step size used). - ! 'FLD' (`Fold function', which vanishes at folds). - ! 'BIF' (`Bifurcation function', which vanishes at singular points). - ! 'HBF' (`Hopf function'; which vanishes at Hopf points). - ! 'SPB' ( Function which vanishes at secondary periodic bifurcations). - !---------------------------------------------------------------------- +\t!--------- ---- + +\tIMPLICIT NONE +\tINTEGER, INTENT(IN) :: NDIM +\tDOUBLE PRECISION, INTENT(INOUT) :: U(NDIM) +\tDOUBLE PRECISION, INTENT(INOUT) :: PAR(*) +\tDOUBLE PRECISION :: GETP,pi,realfm,imagfm,imagfm1 +\tDOUBLE PRECISION :: lw,lw1 +\tLOGICAL, SAVE :: first = .TRUE. +\tDOUBLE PRECISION :: T +\tINTEGER :: i + +\tIF (first) THEN +\t\tCALL STPNT(NDIM,U,PAR,T) +\t\tfirst = .FALSE. +\tENDIF + +\t!PAR(26)=U(44) +\t!PAR(27)=U(52) +\tPAR(25)=0. +\tpi = 4*ATAN(1d0) +\ti=1 +\tlw=100. +\tlw1=101. +\tDO WHILE(i < NDIM) +\t\trealfm = GETP('EIG',I*2-1,U) +\t\tIF (ABS(realfm) < lw) THEN +\t\t\tlw = ABS(realfm) +\t\t\tlw1 = ABS(GETP('EIG',(I+1)*2-1,U)) +\t\t\timagfm1 = ABS(GETP('EIG',(I+1)*2,U)) +\t\t\timagfm = ABS(GETP('EIG',I*2,U)) +\t\tEND IF +\t\ti=i+1 +\tEND DO +\tIF ((lw==lw1).AND.(imagfm1==imagfm).AND.(imagfm/=0.D0)) THEN +\tPAR(25) = 2*pi/imagfm +\tENDIF +\t!---------------------------------------------------------------------- +\t! NOTE : +\t! Parameters set in this subroutine should be considered as ``solution +\t! measures'' and be used for output purposes only. +\t! +\t! They should never be used as `true'' continuation parameters. +\t! +\t! They may, however, be added as ``over-specified parameters'' in the +\t! parameter list associated with the AUTO-Constant NICP, in order to +\t! print their values on the screen and in the ``p.xxx file. +\t! +\t! They may also appear in the list associated with AUTO-Constant NUZR. +\t! +\t!---------------------------------------------------------------------- +\t! For algebraic problems the argument U is, as usual, the state vector. +\t! For differential equations the argument U represents the approximate +\t! solution on the entire interval [0,1]. In this case its values must +\t! be accessed indirectly by calls to GETP, as illustrated below. +\t!---------------------------------------------------------------------- +\t! +\t! Set PAR(2) equal to the L2-norm of U(1) +\t!X PAR(2)=GETP('NRM',1,U) +\t! +\t! Set PAR(3) equal to the minimum of U(2) +\t!X PAR(3)=GETP('MIN',2,U) +\t! +\t! Set PAR(4) equal to the value of U(2) at the left boundary. +\t!X PAR(4)=GETP('BV0',2,U) +\t! +\t! Set PAR(5) equal to the pseudo-arclength step size used. +\t!X PAR(5)=GETP('STP',1,U) +\t! +\t!---------------------------------------------------------------------- +\t! The first argument of GETP may be one of the following: +\t! 'NRM' (L2-norm), 'MAX' (maximum), +\t! 'INT' (integral), 'BV0 (left boundary value), +\t! 'MIN' (minimum), 'BV1' (right boundary value). +\t! +\t! Also available are +\t! 'STP' (Pseudo-arclength step size used). +\t! 'FLD' (`Fold function', which vanishes at folds). +\t! 'BIF' (`Bifurcation function', which vanishes at singular points). +\t! 'HBF' (`Hopf function'; which vanishes at Hopf points). +\t! 'SPB' ( Function which vanishes at secondary periodic bifurcations). +\t!---------------------------------------------------------------------- END SUBROUTINE PVLS """ diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index a09a80c..400e01a 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -17,7 +17,6 @@ from sympy.tensor.array import ImmutableSparseNDimArray # TODO: Check non stored IP version of this -# Jonathan: All public functions should have a docstring class SymbolicQgsTensor(object): @@ -707,6 +706,18 @@ def _set_symbolic_tensor(self): @staticmethod def remove_dic_zeros(dic): + """Removes zero values from dictionary + + Parameters + ---------- + tensor: dict + dictionary which could include 0 in values + Returns + ------- + ten_out: dict + dictionary with same keys and values as input, but keys with value of 0 are removed + """ + non_zero_dic = dict() for key in dic.keys(): if dic[key] != 0: @@ -716,6 +727,18 @@ def remove_dic_zeros(dic): @staticmethod def jacobian_from_dict(dic): + """Calculates the Jacobian from the qgs tensor + + Parameters + ---------- + dic: dict + dictionary of tendencies of the model + Returns + ------- + dic_jac: dict + Jacobian tensor stored in a dictionary + """ + rank = max([len(i) for i in dic.keys()]) n_perm = rank - 2 @@ -736,6 +759,19 @@ def jacobian_from_dict(dic): @staticmethod def simplify_dict(dic): + """calculates the upper triangular tensor of a given tensor, stored in dictionary + + Parameters + ---------- + dic: dict + dictionary of tendencies of the model + + Returns + ------- + dic_upp: dict + Upper triangular tensor, stored as a tensor where the keys are the coordinates of the corresponding value. + """ + keys = dic.keys() dic_upp = dict() @@ -764,10 +800,11 @@ def sub_tensor(self, tensor=None, continuation_variables=None): Parameters ---------- - tensor: dict, sympy array # Jonathan: be more precise here + tensor: dict or Sympy ImmutableSparseNDimArray continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) - If None all variables are substituted. This variable is the opposite of 'variables'. # Jonathan: what does it means? + Variables which remain symbolic, all other variables are substituted with numerical values. + If None all variables are substituted. Returns ------- @@ -1201,7 +1238,8 @@ def compute_tensor(self): class SymbolicQgsTensorT4(SymbolicQgsTensor): # TODO: this takes a long time (>1hr) to run. I think we need a better way to run the non-stored z, v, Z, V IPs. Maybe do not allow `n` as a continuation parameter for this version? - # Jonathan: Could be done by raising a warning if so. + # TODO: Create a warning about long run-times. + """qgs dynamical temperature first order (linear) symbolic tendencies tensor class. Parameters From da18fa8e18570e45167760d7dca9ea83661a355e Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 25 Oct 2023 15:47:01 +0200 Subject: [PATCH 091/143] Bug fix for auto output --- qgs/functions/symbolic_output.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 97c5029..811065f 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -554,14 +554,17 @@ def _split_equations(eq_dict, f_output, line_len=80): for n, eq in enumerate(eq_dict.values()): # split equations to be a maximum of `line_len` - + # split remainder of equation into chunks of length `line_length` eq_chunks = [eq[x: x + line_len] for x in range(0, len(eq), line_len)] - f_output.append('\tF('+str(n+1)+') =\t ' + eq_chunks[0] + "&") - for ln in eq_chunks[1:-1]: - f_output.append("\t\t&" + ln + "&") - - f_output.append("\t\t&" + eq_chunks[-1]) + if len(eq_chunks) > 1: + f_output.append('\tF(' + str(n + 1) + ') =\t ' + eq_chunks[0] + "&") + for ln in eq_chunks[1:-1]: + f_output.append("\t\t&" + ln + "&") + + f_output.append("\t\t&" + eq_chunks[-1]) + else: + f_output.append('\tF(' + str(n + 1) + ') =\t ' + eq_chunks[0]) f_output.append('') return f_output From 1d7348af9c25bb188533f9a8090a3678b193e7ae Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 25 Oct 2023 16:30:46 +0200 Subject: [PATCH 092/143] Error fixed in funciton print out --- notebooks/Symbolic Output-Linear.ipynb | 4 ++-- qgs/functions/symbolic_output.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index 9995430..f265020 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -409,9 +409,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "venv_amoc", "language": "python", - "name": "python3" + "name": "venv_amoc" }, "language_info": { "codemirror_mode": { diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 811065f..94fe17f 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -353,7 +353,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('\tF = np.empty_like(U)') for v in continuation_variables: - f_output.append('\t' + str(v) + " = kwargs['" + str(v.symbol) + "']") + f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") for n, eq in enumerate(eq_dict.values()): f_output.append('\tF['+str(n)+'] = ' + str(eq)) @@ -378,7 +378,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('\t#Tendency function of the qgs model') for v in continuation_variables: - f_output.append('\t' + str(v) + " = kwargs['" + str(v.symbol) + "']") + f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") for n, eq in enumerate(eq_dict.values()): f_output.append('\tdu['+str(n+1)+'] = ' + str(eq)) From 5d7c88898fb54c86e7f957c0a69bb3c7a3a48f1c Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 26 Oct 2023 11:40:45 +0200 Subject: [PATCH 093/143] Merge including AUTO edits --- qgs/functions/symbolic_output.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 94fe17f..76da0d5 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -341,6 +341,8 @@ def equation_as_function(equations, params, string_output=True, language='python a lambdified python function. """ + if continuation_variables is None: + continuation_variables = list() eq_dict = format_equations(equations, params, language=language) @@ -480,16 +482,20 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa # make list of parameters var_list = list() var_ini = list() + sol_ini = list() for i, v in enumerate(continuation_variables): - - temp_str = "PAR(" + str(i) + ") = " + str(v.symbol) - - initial_value = "PAR(" + str(i) + ") = " + str(v) + " Variable: " + str(v.symbol) + temp_str = str(v.symbol) + " = PAR(" + str(i) + ")" + initial_value = "PAR(" + str(i) + ") = " + str(v) + " ! Variable: " + str(v.symbol) var_list.append(temp_str) var_ini.append(initial_value) + for i in range(1, params.ndim+1): + initial_sol = "U(" + str(i) + ") = 0.0d0" + + sol_ini.append(initial_sol) + # Writing model file ################ if auto_main_template is not None: @@ -511,6 +517,9 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa elif 'INITIALISE PARAMETERS' in ln: for iv in var_ini: auto_file.append('\t' + iv) + elif 'INITIALISE SOLUTION' in ln: + for iv in sol_ini: + auto_file.append('\t' + iv) else: auto_file.append(ln.replace('\n', '')) @@ -668,7 +677,9 @@ def _variable_names(params): ! INITIALISE PARAMETERS \t! Initialize the solution -\tU = 0.0d0 + +! INITIALISE SOLUTION + \t! Initialization from a solution file (selection with PAR36) \t! open(unit=15,file='',status='old') \t! is=int(PAR(36)) From 7274f8ec974cda2b993cdc83fb485593298cae80 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 26 Oct 2023 14:05:07 +0200 Subject: [PATCH 094/143] Auto mistakes fixed --- qgs/functions/symbolic_output.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 76da0d5..ddd4c51 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -25,7 +25,7 @@ } fortran_lang_translation = { - '**': '^' + 'conjugate': 'CONJG' # TODO: may need to add variable for pi } @@ -485,8 +485,8 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa sol_ini = list() for i, v in enumerate(continuation_variables): - temp_str = str(v.symbol) + " = PAR(" + str(i) + ")" - initial_value = "PAR(" + str(i) + ") = " + str(v) + " ! Variable: " + str(v.symbol) + temp_str = str(v.symbol) + " = PAR(" + str(i+1) + ")" + initial_value = "PAR(" + str(i+1) + ") = " + str(v) + " ! Variable: " + str(v.symbol) var_list.append(temp_str) var_ini.append(initial_value) From a652e2823ea44a56002bb8f272f6428d535bd719 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sun, 19 Nov 2023 12:15:21 +0100 Subject: [PATCH 095/143] Solving a bug in the AUTO config file --- qgs/functions/symbolic_output.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index ddd4c51..9857109 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -533,7 +533,7 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa auto_config = list() for ln in lines: if '! PARAMETERS' in ln: - auto_config.append('parnames = ' + str({i+1: v.symbol for i, v in enumerate(continuation_variables)})) + auto_config.append('parnames = ' + str({i+1: str(v.symbol) for i, v in enumerate(continuation_variables)})) elif '! VARIABLES' in ln: auto_config.append('unames = ' + str(_variable_names(params))) @@ -542,15 +542,15 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa auto_config.append('NDIM = ' + str(params.ndim)) elif '! CONTINUATION ORDER' in ln: - auto_config.append('ICP = ' + str([v.symbol for v in continuation_variables])) + auto_config.append('ICP = ' + str([str(v.symbol) for v in continuation_variables])) elif '! SOLUTION SAVE' in ln: auto_config.append("# ! User to input save locations") - auto_config.append('UZR = ' + str({v.symbol: [] for v in continuation_variables})) + auto_config.append('UZR = ' + str({str(v.symbol): [] for v in continuation_variables})) elif '! STOP CONDITIONS' in ln: auto_config.append("# ! User to input variable bounds") - auto_config.append('UZSTOP = ' + str({v.symbol: [] for v in continuation_variables})) + auto_config.append('UZSTOP = ' + str({str(v.symbol): [] for v in continuation_variables})) else: auto_config.append(ln.replace('\n', '')) @@ -578,6 +578,7 @@ def _split_equations(eq_dict, f_output, line_len=80): return f_output +# Jonathan: why not use QgParams.var_string instead? def _variable_names(params): # Function to make the variable names for auto num_v = params.number_of_variables From aba77503f3ff06549229115f16a25e370c103ed5 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sun, 19 Nov 2023 19:06:26 +0100 Subject: [PATCH 096/143] Added some AUTO standard parameters name --- qgs/functions/symbolic_output.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 9857109..42d8491 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -533,7 +533,9 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa auto_config = list() for ln in lines: if '! PARAMETERS' in ln: - auto_config.append('parnames = ' + str({i+1: str(v.symbol) for i, v in enumerate(continuation_variables)})) + params_dic = {i+1: str(v.symbol) for i, v in enumerate(continuation_variables)} + params_dic.update({11: 'T', 12: 'theta', 14: 't', 25: 'T_r'}) + auto_config.append('parnames = ' + str(params_dic)) elif '! VARIABLES' in ln: auto_config.append('unames = ' + str(_variable_names(params))) From d03bd81c3a63b72599df47d829d98b219103a82e Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sun, 19 Nov 2023 19:34:05 +0100 Subject: [PATCH 097/143] Adding a first notebook to show AUTO computation --- .../Symbolic Output-Linear-ground-AUTO.ipynb | 1062 +++++++++++++++++ 1 file changed, 1062 insertions(+) create mode 100644 notebooks/Symbolic Output-Linear-ground-AUTO.ipynb diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb new file mode 100644 index 0000000..95e9fcc --- /dev/null +++ b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb @@ -0,0 +1,1062 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "037e82a6", + "metadata": {}, + "source": [ + "# Linear symbolic Land-atmosphere example " + ] + }, + { + "cell_type": "markdown", + "id": "57e1a1bb", + "metadata": {}, + "source": [ + "Testing platform for the symbolic equation version of the qgs model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12f59a3f", + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "import glob\n", + "sys.path.extend([os.path.abspath('../')])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b3814d5", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import sympy as sy\n", + "import sparse as sp\n", + "import math" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7711e102", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.params.params import QgParams\n", + "from qgs.functions.tendencies import create_tendencies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8500ea89", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, GroundSymbolicInnerProducts\n", + "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", + "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf632fac", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters = QgParams({'phi0_npi': np.deg2rad(50.)/np.pi, 'n':1.3 }, dynamic_T=False)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2a8e7c1", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", + "# Mode truncation at the wavenumber 2 in the x and at the \n", + "# wavenumber 4 in the y spatial coordinates for the ocean\n", + "model_parameters.set_ground_channel_fourier_modes(2, 2, mode=\"symbolic\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3237dc76", + "metadata": {}, + "outputs": [], + "source": [ + "# Changing (increasing) the orography depth\n", + "model_parameters.ground_params.set_orography(0.2, 1)\n", + "# Setting the parameters of the heat transfer from the soil\n", + "model_parameters.gotemperature_params.set_params({'gamma': 1.6e7, 'T0': 300})\n", + "model_parameters.atemperature_params.set_params({ 'hlambda':10, 'T0': 290})\n", + "# Setting atmospheric parameters\n", + "model_parameters.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.085, 'kdp': 0.02})\n", + "\n", + "# Setting insolation \n", + "model_parameters.gotemperature_params.set_params({})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51907767", + "metadata": {}, + "outputs": [], + "source": [ + "C_g = 300\n", + "model_parameters.atemperature_params.set_insolation(0.4*C_g , 0)\n", + "\n", + "model_parameters.gotemperature_params.set_insolation(C_g , 0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6112c83a", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "# Takes ~1 mins\n", + "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=False) # <- Can be turned off once saved" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "805e53eb", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=True) # <- Can be turned off once saved" + ] + }, + { + "cell_type": "markdown", + "id": "d43d341b", + "metadata": {}, + "source": [ + "## Outputting model equations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f67ffc01", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations" + ] + }, + { + "cell_type": "markdown", + "id": "8ddc3286", + "metadata": {}, + "source": [ + "Calculating the functions and tensor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8287cf38", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.ground_params.hk[0].symbol" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9479fcb0", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.atemperature_params.eps], language='python')" + ] + }, + { + "cell_type": "markdown", + "id": "0c185e14", + "metadata": {}, + "source": [ + "Convert the dictionary of functions to a lambdified function (not working with numba)\n", + "The input `remain_variables` specifies which variables not to substitute with values." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0fb36604", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "print(funcs)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3ec572eb", + "metadata": {}, + "outputs": [], + "source": [ + "from numba import njit" + ] + }, + { + "cell_type": "markdown", + "id": "2bd791a9", + "metadata": {}, + "source": [ + "Below function has the **kwargs input removed as I cannot get this to work with numba" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5d98d6b", + "metadata": {}, + "outputs": [], + "source": [ + "@njit\n", + "def f(t, U):\n", + "\t#Tendency function of the qgs model\n", + "\tF = np.empty_like(U)\n", + "\tC_go = 300\n", + "\tC_a = 0.4 * C_go\n", + "\tF[0] = -0.0425*U[0] + 0.0425*U[10] - 0.156054828133898*U[12] + 0.156054828133898*U[2]\n", + "\tF[1] = -0.980418808722262*U[0]*U[2] - 0.980418808722262*U[10]*U[12] + 0.0425*U[11] - 1.56867009395562*U[13]*U[15] - 1.50055762081784*U[14]*U[17] + 1.50055762081784*U[15]*U[16] - 0.0425*U[1] + 0.101317695204044*U[2] - 1.56867009395562*U[3]*U[5] - 1.50055762081784*U[4]*U[7] + 1.50055762081784*U[5]*U[6]\n", + "\tF[2] = 0.980418808722262*U[0]*U[1] - 0.0580129472616723*U[0] + 0.980418808722262*U[10]*U[11] + 0.0580129472616723*U[10] + 0.0425*U[12] + 1.56867009395562*U[13]*U[14] + 1.50055762081784*U[14]*U[16] + 1.50055762081784*U[15]*U[17] - 0.101317695204044*U[1] - 0.0425*U[2] + 1.56867009395562*U[3]*U[4] + 1.50055762081784*U[4]*U[6] + 1.50055762081784*U[5]*U[7]\n", + "\tF[3] = 1.87265793760678*U[11]*U[15] - 1.87265793760678*U[12]*U[14] + 0.0425*U[13] - 0.0624219312535594*U[15] + 3.74531587521356*U[16]*U[19] - 3.74531587521356*U[17]*U[18] + 1.87265793760678*U[1]*U[5] - 1.87265793760678*U[2]*U[4] - 0.0425*U[3] + 0.0624219312535594*U[5] + 3.74531587521356*U[6]*U[9] - 3.74531587521356*U[7]*U[8]\n", + "\tF[4] = -1.02902937637678*U[0]*U[5] - 1.02902937637678*U[10]*U[15] + 1.73752196836555*U[11]*U[17] + 0.574852231579351*U[12]*U[13] - 1.73752196836555*U[12]*U[16] + 0.0425*U[14] - 0.0342706502636204*U[17] + 1.73752196836555*U[1]*U[7] + 0.574852231579351*U[2]*U[3] - 1.73752196836555*U[2]*U[6] - 0.0425*U[4] + 0.0478988752370612*U[5] + 0.0342706502636204*U[7]\n", + "\tF[5] = 1.02902937637678*U[0]*U[4] + 1.02902937637678*U[10]*U[14] - 0.574852231579351*U[11]*U[13] - 1.73752196836555*U[11]*U[16] - 1.73752196836555*U[12]*U[17] + 0.0438818497388818*U[13] + 0.0425*U[15] + 0.0342706502636204*U[16] - 0.574852231579351*U[1]*U[3] - 1.73752196836555*U[1]*U[6] - 1.73752196836555*U[2]*U[7] - 0.0438818497388818*U[3] - 0.0478988752370612*U[4] - 0.0425*U[5] - 0.0342706502636204*U[6]\n", + "\tF[6] = -2.71889339738442*U[0]*U[7] - 2.71889339738442*U[10]*U[17] + 0.753865979381443*U[11]*U[15] + 0.753865979381443*U[12]*U[14] - 4.35022943581506*U[13]*U[19] - 0.0251288659793814*U[15] + 0.0425*U[16] + 0.753865979381443*U[1]*U[5] + 0.753865979381443*U[2]*U[4] - 4.35022943581506*U[3]*U[9] + 0.0251288659793814*U[5] - 0.0425*U[6] + 0.0702434536337315*U[7]\n", + "\tF[7] = 2.71889339738442*U[0]*U[6] + 2.71889339738442*U[10]*U[16] - 0.753865979381443*U[11]*U[14] + 0.753865979381443*U[12]*U[15] + 4.35022943581506*U[13]*U[18] + 0.0251288659793814*U[14] + 0.0425*U[17] - 0.753865979381443*U[1]*U[4] + 0.753865979381443*U[2]*U[5] + 4.35022943581506*U[3]*U[8] - 0.0251288659793814*U[4] - 0.0702434536337315*U[6] - 0.0425*U[7]\n", + "\tF[8] = -2.26482546109569*U[0]*U[9] - 2.26482546109569*U[10]*U[19] - 1.7450294536311*U[13]*U[17] + 0.0425*U[18] - 1.7450294536311*U[3]*U[7] - 0.0425*U[8] + 0.050658847602022*U[9]\n", + "\tF[9] = 2.26482546109569*U[0]*U[8] + 2.26482546109569*U[10]*U[18] + 1.7450294536311*U[13]*U[16] + 0.0425*U[19] + 1.7450294536311*U[3]*U[6] - 0.050658847602022*U[8] - 0.0425*U[9]\n", + "\tF[10] = 4.68670500616653e-6*C_a + 0.00386363636363636*U[0] - 0.023715438957012*U[10] - 1.41868025576271*U[11]*U[2] + 1.41868025576271*U[12]*U[1] + 0.0141868025576271*U[12] - 1.13494420461017*U[14]*U[5] + 1.13494420461017*U[15]*U[4] - 2.83736051152543*U[16]*U[7] + 2.83736051152543*U[17]*U[6] - 2.26988840922034*U[18]*U[9] + 2.26988840922034*U[19]*U[8] + 0.00645434108527132*U[20] - 0.0141868025576271*U[2]\n", + "\tF[11] = -1.43757363347933*U[0]*U[12] + 1.02191932371371*U[10]*U[2] - 0.0315441157231782*U[11] + 0.0214771158470353*U[12] + 1.63507091794193*U[13]*U[5] + 1.21855791962175*U[14]*U[7] - 2.30011781356693*U[15]*U[3] - 1.21855791962175*U[15]*U[6] + 1.85472813238771*U[16]*U[5] - 1.85472813238771*U[17]*U[4] + 0.00900906225374311*U[1] + 0.00559477950653936*U[21]\n", + "\tF[12] = 1.43757363347933*U[0]*U[11] + 0.0122974647859652*U[0] - 1.02191932371371*U[10]*U[1] - 0.0122974647859652*U[10] - 0.0214771158470353*U[11] - 0.0315441157231782*U[12] - 1.63507091794193*U[13]*U[4] + 2.30011781356693*U[14]*U[3] - 1.21855791962175*U[14]*U[6] - 1.21855791962175*U[15]*U[7] + 1.85472813238771*U[16]*U[4] + 1.85472813238771*U[17]*U[5] + 0.00559477950653936*U[22] + 0.00900906225374311*U[2]\n", + "\tF[13] = -1.24843862507119*U[11]*U[5] + 1.24843862507119*U[12]*U[4] - 0.0363121306090808*U[13] - 2.3185288751322*U[14]*U[2] + 2.3185288751322*U[15]*U[1] + 0.017834837501017*U[15] - 2.49687725014237*U[16]*U[9] + 2.49687725014237*U[17]*U[8] - 4.63705775026441*U[18]*U[7] + 4.63705775026441*U[19]*U[6] + 0.00507126799557032*U[23] + 0.0121428571428571*U[3] - 0.017834837501017*U[5]\n", + "\tF[14] = -1.16886956037576*U[0]*U[15] + 0.422511733532696*U[10]*U[5] - 0.612715105162524*U[11]*U[7] - 1.38291034440645*U[12]*U[3] + 0.612715105162524*U[12]*U[6] + 1.79985224341047*U[13]*U[2] - 0.0412871146288803*U[14] + 0.0173705927405276*U[15] - 1.87294455066922*U[16]*U[2] + 1.87294455066922*U[17]*U[1] + 0.0124282982791587*U[17] + 0.00452503199094866*U[24] + 0.015412683237731*U[4] - 0.0124282982791587*U[7]\n", + "\tF[15] = 1.16886956037576*U[0]*U[14] - 0.422511733532696*U[10]*U[4] + 1.38291034440645*U[11]*U[3] + 0.612715105162524*U[11]*U[6] + 0.612715105162524*U[12]*U[7] - 1.79985224341047*U[13]*U[1] - 0.0159138129390846*U[13] - 0.0173705927405276*U[14] - 0.0412871146288803*U[15] - 1.87294455066922*U[16]*U[1] - 0.0124282982791587*U[16] - 1.87294455066922*U[17]*U[2] + 0.00452503199094866*U[25] + 0.0159138129390846*U[3] + 0.015412683237731*U[5] + 0.0124282982791587*U[6]\n", + "\tF[16] = -2.94535914360826*U[0]*U[17] + 0.569389237785845*U[10]*U[7] - 0.768581081081081*U[11]*U[5] - 0.768581081081081*U[12]*U[4] + 0.911022780457352*U[13]*U[9] + 1.42736486486486*U[14]*U[2] + 1.42736486486486*U[15]*U[1] + 0.0109797297297297*U[15] - 0.0460906434981493*U[16] + 0.0306919594705944*U[17] - 4.71257462977322*U[19]*U[3] + 0.00399762116767931*U[26] - 0.0109797297297297*U[5] + 0.0185698198198198*U[6]\n", + "\tF[17] = 2.94535914360826*U[0]*U[16] - 0.569389237785845*U[10]*U[6] + 0.768581081081081*U[11]*U[4] - 0.768581081081081*U[12]*U[5] - 0.911022780457352*U[13]*U[8] - 1.42736486486486*U[14]*U[1] - 0.0109797297297297*U[14] + 1.42736486486486*U[15]*U[2] - 0.0306919594705944*U[16] - 0.0460906434981493*U[17] + 4.71257462977322*U[18]*U[3] + 0.00399762116767931*U[27] + 0.0109797297297297*U[4] + 0.0185698198198198*U[7]\n", + "\tF[18] = -2.37660377951895*U[0]*U[19] + 0.0288656329496225*U[10]*U[9] + 1.50101291338039*U[13]*U[7] - 3.30992591155675*U[17]*U[3] - 0.0513521112007289*U[18] + 0.026256705211838*U[19] + 0.00341993024749443*U[28] + 0.0220279383429672*U[8]\n", + "\tF[19] = 2.37660377951895*U[0]*U[18] - 0.0288656329496225*U[10]*U[8] - 1.50101291338039*U[13]*U[6] + 3.30992591155675*U[16]*U[3] - 0.026256705211838*U[18] - 0.0513521112007289*U[19] + 0.00341993024749443*U[29] + 0.0220279383429672*U[9]\n", + "\tF[20] = 6.44421938347898e-6*C_go + 0.0172043158333333*U[10] - 0.00976477713178294*U[20]\n", + "\tF[21] = 0.0172043158333333*U[11] - 0.00976477713178294*U[21]\n", + "\tF[22] = 0.0172043158333333*U[12] - 0.00976477713178294*U[22]\n", + "\tF[23] = 0.0172043158333333*U[13] - 0.00976477713178294*U[23]\n", + "\tF[24] = 0.0172043158333333*U[14] - 0.00976477713178294*U[24]\n", + "\tF[25] = 0.0172043158333333*U[15] - 0.00976477713178294*U[25]\n", + "\tF[26] = 0.0172043158333333*U[16] - 0.00976477713178294*U[26]\n", + "\tF[27] = 0.0172043158333333*U[17] - 0.00976477713178294*U[27]\n", + "\tF[28] = 0.0172043158333333*U[18] - 0.00976477713178294*U[28]\n", + "\tF[29] = 0.0172043158333333*U[19] - 0.00976477713178294*U[29]\n", + "\treturn F" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7cededb7", + "metadata": {}, + "outputs": [], + "source": [ + "offset = 1 if model_parameters.dynamic_T else 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b7e03156", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.ndim" + ] + }, + { + "cell_type": "markdown", + "id": "0579478b", + "metadata": {}, + "source": [ + "## Comparing with numerical results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "77155759", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f38b345f", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0f5941", + "metadata": {}, + "outputs": [], + "source": [ + "integrator = RungeKuttaIntegrator()\n", + "integrator.set_func(f)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "415ab585", + "metadata": {}, + "outputs": [], + "source": [ + "integrator_num = RungeKuttaIntegrator()\n", + "integrator_num.set_func(f_num)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2b5f14a", + "metadata": {}, + "outputs": [], + "source": [ + "# ICs calculated from long transient\n", + "\n", + "ic = np.array([0.05055959, -0.01639403, -0.01440781, -0.01846523, -0.01352099,\n", + " 0.011685 , -0.00201673, -0.02030682, 0.03923588, -0.02229535,\n", + " 0.0586372 , -0.01805569, -0.01264252, -0.0103574 , -0.00618456,\n", + " 0.01159318, -0.00478694, -0.00782509, 0.01066059, -0.01552667,\n", + " 0.30718325, -0.03247899, -0.04512935, -0.00078786, -0.00067468,\n", + " 0.00183836, 0.00068025, 0.00215424, -0.00322845, -0.00186392])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c6d5773", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", + "reference_time, reference_traj = integrator.get_trajectories()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b38989ba", + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator_num.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", + "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f892676", + "metadata": {}, + "outputs": [], + "source": [ + "varx = 2\n", + "vary = 1\n", + "plt.figure(figsize=(12, 10))\n", + "\n", + "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", + "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", + "\n", + "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68781088", + "metadata": {}, + "outputs": [], + "source": [ + "varx = 2\n", + "vary = 1\n", + "plt.figure(figsize=(12, 10))\n", + "\n", + "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", + "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", + "\n", + "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6697d107", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 8))\n", + "t_s = 100000\n", + "\n", + "plt.plot(reference_time_num[:t_s] * model_parameters.dimensional_time, reference_traj_num[varx, :t_s])\n", + "plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "76ecb077", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure(figsize=(12, 8))\n", + "t_s = 1000\n", + "\n", + "vars = [1, 2]\n", + "\n", + "plt.plot(reference_traj_num[vars, :-1].T)\n", + "# plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "da46eccd", + "metadata": {}, + "source": [ + "## Creating AUTO files" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af1b4747", + "metadata": {}, + "outputs": [], + "source": [ + "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.gotemperature_params.C[0], model_parameters.atemperature_params.C[0], model_parameters.atmospheric_params.kd, model_parameters.atmospheric_params.kdp], language='auto')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f2db3f4", + "metadata": {}, + "outputs": [], + "source": [ + "print(funcs[0].split('\\n')[208])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87f86011", + "metadata": {}, + "outputs": [], + "source": [ + "auto_eq_lines = funcs[0].split('\\n')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec60bcec", + "metadata": {}, + "outputs": [], + "source": [ + "auto_eq_lines[34] = '\\tC_a1 = 0.4*C_go1'\n", + "auto_eq_lines[208] = '!'+auto_eq_lines[208]\n", + "auto_eq_lines[209] = '!'+auto_eq_lines[209]\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "626c1862", + "metadata": {}, + "outputs": [], + "source": [ + "auto_eq = '\\n'.join(auto_eq_lines)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78c7ceba", + "metadata": {}, + "outputs": [], + "source": [ + "auto_config_lines = funcs[1].split('\\n')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "065e83b3", + "metadata": {}, + "outputs": [], + "source": [ + "auto_config_lines[-5] = \"UZR = {'C_go1': \" + str(list(np.arange(50.,375.,50.)))+\"}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc781ecc", + "metadata": {}, + "outputs": [], + "source": [ + "auto_config_lines[-2] = \"UZSTOP = {'C_go1': [0.,400.]}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7760092", + "metadata": {}, + "outputs": [], + "source": [ + "auto_config = '\\n'.join(auto_config_lines)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80f05363", + "metadata": {}, + "outputs": [], + "source": [ + "with open('qgs_land-atmosphere_auto.f90', 'w') as ff:\n", + " ff.write(auto_eq)\n", + " \n", + "with open('c.qgs_land-atmosphere_auto', 'w') as ff:\n", + " ff.write(auto_config)" + ] + }, + { + "cell_type": "markdown", + "id": "3f219527", + "metadata": {}, + "source": [ + "## Defining some useful functions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29fe26ab", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_branches(filename, variables=(0,1), ax=None, figsize=(10, 8), markersize=12., plot_kwargs=None, marker_kwargs=None, branch_indices='all', excluded_labels=('UZ', 'EP', 'No Label'), variables_name=None):\n", + " \n", + " if ax is None:\n", + " fig = plt.figure(figsize=figsize)\n", + " ax = fig.gca()\n", + " \n", + " if plot_kwargs is None:\n", + " plot_kwargs = dict()\n", + " \n", + " if marker_kwargs is None:\n", + " marker_kwargs = dict()\n", + " \n", + " pb_obj = parseB.parseB()\n", + " fb = open(filename, 'r')\n", + " pb_obj.read(fb)\n", + " \n", + " keys = list(pb_obj.branches[0].keys())\n", + " \n", + " if variables[0] in keys:\n", + " var1 = variables[0]\n", + " else:\n", + " try:\n", + " var1 = keys[variables[0]]\n", + " except:\n", + " var1 = keys[0]\n", + "\n", + " if variables[1] in keys:\n", + " var2 = variables[1]\n", + " else:\n", + " try:\n", + " var2 = keys[variables[1]]\n", + " except:\n", + " var2 = keys[1]\n", + "\n", + " if branch_indices == 'all':\n", + " branch_indices = range(len(pb_obj.branches))\n", + "\n", + " branch_num = list()\n", + " for i in branch_indices:\n", + " branch_dict = pb_obj.branches[i].todict()\n", + " branch_num.append(pb_obj.branches[i]['BR'])\n", + "\n", + " labels = list()\n", + " for j, coords in enumerate(zip(branch_dict[var1], branch_dict[var2])):\n", + " lab = pb_obj.branches[i].labels[j]\n", + " if not lab:\n", + " pass\n", + " else:\n", + " labels.append((coords, list(lab.keys())[0]))\n", + "\n", + " ax.plot(branch_dict[var1], branch_dict[var2], **plot_kwargs)\n", + " if excluded_labels != 'all':\n", + " for label in labels:\n", + " coords = label[0]\n", + " lab = label[1]\n", + " if lab not in excluded_labels:\n", + " ax.text(coords[0], coords[1], r'${\\bf '+ lab + r'}$', fontdict={'family':'sans-serif','size':markersize},va='center', ha='center', **marker_kwargs, clip_on=True)\n", + " \n", + " fb.close()\n", + " if variables_name is None:\n", + " ax.set_xlabel(var1)\n", + " ax.set_ylabel(var2)\n", + " else:\n", + " if isinstance(variables_name, dict):\n", + " ax.set_xlabel(variables_name[var1])\n", + " ax.set_ylabel(variables_name[var2])\n", + " else:\n", + " ax.set_xlabel(variables_name[0])\n", + " ax.set_ylabel(variables_name[1])\n", + " return ax, branch_num" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1b0d5cb", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_branches3d(filename, variables=(0,1,3), ax=None, figsize=(10, 8), markersize=12., plot_kwargs=None, marker_kwargs=None, branch_indices='all', excluded_labels=('UZ', 'EP', 'No Label'), variables_name=None):\n", + " \n", + " if ax is None:\n", + " fig = plt.figure(figsize=figsize)\n", + " ax = plt.subplot(projection='3d')\n", + " \n", + " if plot_kwargs is None:\n", + " plot_kwargs = dict()\n", + " \n", + " if marker_kwargs is None:\n", + " marker_kwargs = dict()\n", + " \n", + " pb_obj = parseB.parseB()\n", + " fb = open(filename, 'r')\n", + " pb_obj.read(fb)\n", + " \n", + " keys = list(pb_obj.branches[0].keys())\n", + " \n", + " if variables[0] in keys:\n", + " var1 = variables[0]\n", + " else:\n", + " try:\n", + " var1 = keys[variables[0]]\n", + " except:\n", + " var1 = keys[0]\n", + "\n", + " if variables[1] in keys:\n", + " var2 = variables[1]\n", + " else:\n", + " try:\n", + " var2 = keys[variables[1]]\n", + " except:\n", + " var2 = keys[1]\n", + " \n", + " if variables[2] in keys:\n", + " var3 = variables[2]\n", + " else:\n", + " try:\n", + " var3 = keys[variables[2]]\n", + " except:\n", + " var3 = keys[2]\n", + "\n", + "\n", + " if branch_indices == 'all':\n", + " branch_indices = range(len(pb_obj.branches))\n", + "\n", + " branch_num = list()\n", + " for i in branch_indices:\n", + " branch_dict = pb_obj.branches[i].todict()\n", + " branch_num.append(pb_obj.branches[i]['BR'])\n", + "\n", + " labels = list()\n", + " for j, coords in enumerate(zip(branch_dict[var1], branch_dict[var2], branch_dict[var3])):\n", + " lab = pb_obj.branches[i].labels[j]\n", + " if not lab:\n", + " pass\n", + " else:\n", + " labels.append((coords, list(lab.keys())[0]))\n", + "\n", + " ax.plot(branch_dict[var1], branch_dict[var2], branch_dict[var3], **plot_kwargs)\n", + " if excluded_labels != 'all':\n", + " for label in labels:\n", + " coords = label[0]\n", + " lab = label[1]\n", + " if lab not in excluded_labels:\n", + " ax.text(coords[0], coords[1], coords[2], r'${\\bf '+ lab + r'}$', fontdict={'family':'sans-serif','size':markersize},va='center', ha='center', **marker_kwargs, clip_on=True)\n", + " \n", + " fb.close()\n", + " if variables_name is None:\n", + " ax.set_xlabel(var1)\n", + " ax.set_ylabel(var2)\n", + " ax.set_zlabel(var3)\n", + " else:\n", + " if isinstance(variables_name, dict):\n", + " ax.set_xlabel(variables_name[var1])\n", + " ax.set_ylabel(variables_name[var2])\n", + " ax.set_zlabel(variables_name[var3])\n", + " else:\n", + " ax.set_xlabel(variables_name[0])\n", + " ax.set_ylabel(variables_name[1])\n", + " ax.set_zlabel(variables_name[2])\n", + " return ax, branch_num" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff2a41a0", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_branch_vs_others(branch_num, figsize=(10, 16), excluded_labels=('UZ', 'EP', 'No Label')):\n", + " \n", + " fig = plt.figure(figsize=figsize)\n", + " ax = plt.subplot(2,1,1)\n", + " ax3 = plt.subplot(2,1,2, projection='3d')\n", + " \n", + " \n", + " \n", + " fp = glob.glob(nb_dir + '/b.fp*')\n", + " print(fp)\n", + " for i in range(len(fp)-1,-1,-1):\n", + " if '~' in fp[i]:\n", + " del fp[i]\n", + " \n", + " for i in range(len(fp)-1,-1,-1):\n", + " \n", + " try:\n", + " num = int(fp[i][-2:])\n", + " except:\n", + " num = int(fp[i][-1])\n", + " \n", + " if num == branch_num:\n", + " plot_branches(fp[i], ax=ax, plot_kwargs={'color': 'tab:blue', 'zorder': 10.}, variables=(0, 1), variables_name=(r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=excluded_labels)\n", + " plot_branches3d(fp[i], ax=ax3, plot_kwargs={'color': 'tab:blue', 'zorder': 10.}, variables=(3, 0, 1), variables_name=(r'$\\psi_{{\\rm a}, 2}$', r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=excluded_labels)\n", + " else:\n", + " plot_branches(fp[i], ax=ax, plot_kwargs={'color': 'tab:orange'}, variables=(0, 1), variables_name=(r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=\"all\")\n", + " plot_branches3d(fp[i], ax=ax3, plot_kwargs={'color': 'tab:orange'}, variables=(3, 0, 1), variables_name=(r'$\\psi_{{\\rm a}, 2}$', r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=\"all\")\n", + "\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "2d4e97e3", + "metadata": {}, + "source": [ + "## AUTO analysis" + ] + }, + { + "cell_type": "markdown", + "id": "613647e4", + "metadata": {}, + "source": [ + "Initializing AUTO" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b291604", + "metadata": {}, + "outputs": [], + "source": [ + "auto_directory = os.environ['AUTO_DIR']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a70a505f", + "metadata": {}, + "outputs": [], + "source": [ + "sys.path.append(auto_directory + '/python/auto')\n", + "sys.path.append(auto_directory + '/python')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1ddf964", + "metadata": {}, + "outputs": [], + "source": [ + "import AUTOCommands as ac\n", + "import AUTOclui as acl\n", + "import interactiveBindings as ib\n", + "import runAUTO as ra\n", + "import parseB, parseC, parseD, parseS, parseBandS" + ] + }, + { + "cell_type": "markdown", + "id": "a30354af", + "metadata": {}, + "source": [ + "Loading the model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2700819a", + "metadata": {}, + "outputs": [], + "source": [ + "nb_dir = !pwd\n", + "nb_dir = nb_dir[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4f451e7", + "metadata": {}, + "outputs": [], + "source": [ + "lf=glob.glob(nb_dir + '/c.*')\n", + "for i in range(len(lf)-1,-1,-1):\n", + " if '~' in lf[i]:\n", + " del lf[i]\n", + "\n", + "y=lf[0]\n", + "mname=y[len(nb_dir)+3:]\n", + "print(\"Loading model \"+mname)" + ] + }, + { + "cell_type": "markdown", + "id": "539f2d00", + "metadata": {}, + "source": [ + "Starting a runner" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1240ace5", + "metadata": {}, + "outputs": [], + "source": [ + "runner = ra.runAUTO()\n", + "ac.load(mname, runner=runner)" + ] + }, + { + "cell_type": "markdown", + "id": "672ef822", + "metadata": {}, + "source": [ + "Finding the first branch of fixed point" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe0c0600", + "metadata": {}, + "outputs": [], + "source": [ + "x=ac.run(mname, runner=runner)\n", + "ac.save(x,'fp1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "375f5335", + "metadata": {}, + "outputs": [], + "source": [ + "plot_branch_vs_others(0)" + ] + }, + { + "cell_type": "markdown", + "id": "7f2075c3", + "metadata": {}, + "source": [ + "## Computing the periodic orbits (POs) out of the fixed point" + ] + }, + { + "cell_type": "markdown", + "id": "e00931b3", + "metadata": {}, + "source": [ + "Loading the branch and printing the summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b535d9be", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "r = ac.loadbd('fp1')\n", + "print(r.summary())" + ] + }, + { + "cell_type": "markdown", + "id": "12205b02", + "metadata": {}, + "source": [ + "Listing the Hopf bifurcation points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12261493", + "metadata": {}, + "outputs": [], + "source": [ + "solutions_list = list()\n", + "ps_obj = parseS.parseS('./s.fp1')\n", + "pc_full_obj = parseC.parseC('c.'+mname)\n", + "for i in range(len(ps_obj)):\n", + " s = ps_obj[i].load(constants=pc_full_obj)\n", + " if s['TY'] == 'HB':\n", + " solutions_list.append(s)\n", + " \n", + "\n", + "# reversing to get it in Co increasing order\n", + "solutions_list = solutions_list[::-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3932386", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "solutions_list" + ] + }, + { + "cell_type": "markdown", + "id": "91199f37", + "metadata": {}, + "source": [ + "### Computing the first Hopf bifurcation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2604fb2", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "s = solutions_list[0]\n", + "rh=ac.run(s,ICP=['C_go1', 'T'], IPS=2, NTST=400, NMX=0, runner=runner)\n", + "ac.save(rh, 'fp1_hp1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0593b6a8", + "metadata": {}, + "outputs": [], + "source": [ + "plot_branches('./b.fp1_hp1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9f2918d4", + "metadata": {}, + "outputs": [], + "source": [ + "plot_branches('s.')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 6304452df15ca350f52a3b2c24f33baa437cb1a7 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 20 Nov 2023 10:10:30 +0100 Subject: [PATCH 098/143] Letting the user to choose if params and solutions are initialized in AUTO file. --- qgs/functions/symbolic_output.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 42d8491..99a7e48 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -436,7 +436,8 @@ def equation_as_function(equations, params, string_output=True, language='python return f_output -def create_auto_file(equations, params, continuation_variables, auto_main_template=None, auto_c_template=None): +def create_auto_file(equations, params, continuation_variables, auto_main_template=None, auto_c_template=None, + initialize_params=False, initialize_solution=False): """Creates the auto configuration file and the model file. Saves files to specified folder. @@ -454,6 +455,10 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa auto_c_template: str, optional The template to be used to generate the AUTO config file. If not provided, use the default template. + initialize_params: bool, optional + Add lines in the AUTO STPNT function to initialize the parameters. Default to `False`. + initialize_solution: bool, optional + Add lines in the AUTO STPNT function to initialize the solution. Default to `False`. Returns ------- @@ -514,10 +519,10 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa elif 'EVOLUTION EQUATIONS' in ln: for e in equations: auto_file.append(e) - elif 'INITIALISE PARAMETERS' in ln: + elif 'INITIALISE PARAMETERS' in ln and initialize_params: for iv in var_ini: auto_file.append('\t' + iv) - elif 'INITIALISE SOLUTION' in ln: + elif 'INITIALISE SOLUTION' in ln and initialize_solution: for iv in sol_ini: auto_file.append('\t' + iv) else: @@ -805,13 +810,11 @@ def _variable_names(params): \tDOUBLE PRECISION :: T \tINTEGER :: i -\tIF (first) THEN -\t\tCALL STPNT(NDIM,U,PAR,T) -\t\tfirst = .FALSE. -\tENDIF +\t!IF (first) THEN +\t\t!CALL STPNT(NDIM,U,PAR,T) +\t\t!first = .FALSE. +\t!ENDIF -\t!PAR(26)=U(44) -\t!PAR(27)=U(52) \tPAR(25)=0. \tpi = 4*ATAN(1d0) \ti=1 From 709c0d74b81f5098f03e68d2f710258817eb1c93 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 20 Nov 2023 10:23:49 +0100 Subject: [PATCH 099/143] Update of the AUTO notebook --- .../Symbolic Output-Linear-ground-AUTO.ipynb | 406 ++---------------- 1 file changed, 31 insertions(+), 375 deletions(-) diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb index 95e9fcc..7c6f69a 100644 --- a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb +++ b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb @@ -36,9 +36,7 @@ "outputs": [], "source": [ "import numpy as np\n", - "import sympy as sy\n", - "import sparse as sp\n", - "import math" + "import matplotlib.pyplot as plt" ] }, { @@ -64,6 +62,16 @@ "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e03aa6e", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.functions.symbolic_output import create_symbolic_equations" + ] + }, { "cell_type": "code", "execution_count": null, @@ -119,348 +127,6 @@ "model_parameters.gotemperature_params.set_insolation(C_g , 0)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "6112c83a", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "# Takes ~1 mins\n", - "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=False) # <- Can be turned off once saved" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "805e53eb", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=True) # <- Can be turned off once saved" - ] - }, - { - "cell_type": "markdown", - "id": "d43d341b", - "metadata": {}, - "source": [ - "## Outputting model equations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f67ffc01", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations" - ] - }, - { - "cell_type": "markdown", - "id": "8ddc3286", - "metadata": {}, - "source": [ - "Calculating the functions and tensor" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8287cf38", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.ground_params.hk[0].symbol" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9479fcb0", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.atemperature_params.eps], language='python')" - ] - }, - { - "cell_type": "markdown", - "id": "0c185e14", - "metadata": {}, - "source": [ - "Convert the dictionary of functions to a lambdified function (not working with numba)\n", - "The input `remain_variables` specifies which variables not to substitute with values." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0fb36604", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "print(funcs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ec572eb", - "metadata": {}, - "outputs": [], - "source": [ - "from numba import njit" - ] - }, - { - "cell_type": "markdown", - "id": "2bd791a9", - "metadata": {}, - "source": [ - "Below function has the **kwargs input removed as I cannot get this to work with numba" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5d98d6b", - "metadata": {}, - "outputs": [], - "source": [ - "@njit\n", - "def f(t, U):\n", - "\t#Tendency function of the qgs model\n", - "\tF = np.empty_like(U)\n", - "\tC_go = 300\n", - "\tC_a = 0.4 * C_go\n", - "\tF[0] = -0.0425*U[0] + 0.0425*U[10] - 0.156054828133898*U[12] + 0.156054828133898*U[2]\n", - "\tF[1] = -0.980418808722262*U[0]*U[2] - 0.980418808722262*U[10]*U[12] + 0.0425*U[11] - 1.56867009395562*U[13]*U[15] - 1.50055762081784*U[14]*U[17] + 1.50055762081784*U[15]*U[16] - 0.0425*U[1] + 0.101317695204044*U[2] - 1.56867009395562*U[3]*U[5] - 1.50055762081784*U[4]*U[7] + 1.50055762081784*U[5]*U[6]\n", - "\tF[2] = 0.980418808722262*U[0]*U[1] - 0.0580129472616723*U[0] + 0.980418808722262*U[10]*U[11] + 0.0580129472616723*U[10] + 0.0425*U[12] + 1.56867009395562*U[13]*U[14] + 1.50055762081784*U[14]*U[16] + 1.50055762081784*U[15]*U[17] - 0.101317695204044*U[1] - 0.0425*U[2] + 1.56867009395562*U[3]*U[4] + 1.50055762081784*U[4]*U[6] + 1.50055762081784*U[5]*U[7]\n", - "\tF[3] = 1.87265793760678*U[11]*U[15] - 1.87265793760678*U[12]*U[14] + 0.0425*U[13] - 0.0624219312535594*U[15] + 3.74531587521356*U[16]*U[19] - 3.74531587521356*U[17]*U[18] + 1.87265793760678*U[1]*U[5] - 1.87265793760678*U[2]*U[4] - 0.0425*U[3] + 0.0624219312535594*U[5] + 3.74531587521356*U[6]*U[9] - 3.74531587521356*U[7]*U[8]\n", - "\tF[4] = -1.02902937637678*U[0]*U[5] - 1.02902937637678*U[10]*U[15] + 1.73752196836555*U[11]*U[17] + 0.574852231579351*U[12]*U[13] - 1.73752196836555*U[12]*U[16] + 0.0425*U[14] - 0.0342706502636204*U[17] + 1.73752196836555*U[1]*U[7] + 0.574852231579351*U[2]*U[3] - 1.73752196836555*U[2]*U[6] - 0.0425*U[4] + 0.0478988752370612*U[5] + 0.0342706502636204*U[7]\n", - "\tF[5] = 1.02902937637678*U[0]*U[4] + 1.02902937637678*U[10]*U[14] - 0.574852231579351*U[11]*U[13] - 1.73752196836555*U[11]*U[16] - 1.73752196836555*U[12]*U[17] + 0.0438818497388818*U[13] + 0.0425*U[15] + 0.0342706502636204*U[16] - 0.574852231579351*U[1]*U[3] - 1.73752196836555*U[1]*U[6] - 1.73752196836555*U[2]*U[7] - 0.0438818497388818*U[3] - 0.0478988752370612*U[4] - 0.0425*U[5] - 0.0342706502636204*U[6]\n", - "\tF[6] = -2.71889339738442*U[0]*U[7] - 2.71889339738442*U[10]*U[17] + 0.753865979381443*U[11]*U[15] + 0.753865979381443*U[12]*U[14] - 4.35022943581506*U[13]*U[19] - 0.0251288659793814*U[15] + 0.0425*U[16] + 0.753865979381443*U[1]*U[5] + 0.753865979381443*U[2]*U[4] - 4.35022943581506*U[3]*U[9] + 0.0251288659793814*U[5] - 0.0425*U[6] + 0.0702434536337315*U[7]\n", - "\tF[7] = 2.71889339738442*U[0]*U[6] + 2.71889339738442*U[10]*U[16] - 0.753865979381443*U[11]*U[14] + 0.753865979381443*U[12]*U[15] + 4.35022943581506*U[13]*U[18] + 0.0251288659793814*U[14] + 0.0425*U[17] - 0.753865979381443*U[1]*U[4] + 0.753865979381443*U[2]*U[5] + 4.35022943581506*U[3]*U[8] - 0.0251288659793814*U[4] - 0.0702434536337315*U[6] - 0.0425*U[7]\n", - "\tF[8] = -2.26482546109569*U[0]*U[9] - 2.26482546109569*U[10]*U[19] - 1.7450294536311*U[13]*U[17] + 0.0425*U[18] - 1.7450294536311*U[3]*U[7] - 0.0425*U[8] + 0.050658847602022*U[9]\n", - "\tF[9] = 2.26482546109569*U[0]*U[8] + 2.26482546109569*U[10]*U[18] + 1.7450294536311*U[13]*U[16] + 0.0425*U[19] + 1.7450294536311*U[3]*U[6] - 0.050658847602022*U[8] - 0.0425*U[9]\n", - "\tF[10] = 4.68670500616653e-6*C_a + 0.00386363636363636*U[0] - 0.023715438957012*U[10] - 1.41868025576271*U[11]*U[2] + 1.41868025576271*U[12]*U[1] + 0.0141868025576271*U[12] - 1.13494420461017*U[14]*U[5] + 1.13494420461017*U[15]*U[4] - 2.83736051152543*U[16]*U[7] + 2.83736051152543*U[17]*U[6] - 2.26988840922034*U[18]*U[9] + 2.26988840922034*U[19]*U[8] + 0.00645434108527132*U[20] - 0.0141868025576271*U[2]\n", - "\tF[11] = -1.43757363347933*U[0]*U[12] + 1.02191932371371*U[10]*U[2] - 0.0315441157231782*U[11] + 0.0214771158470353*U[12] + 1.63507091794193*U[13]*U[5] + 1.21855791962175*U[14]*U[7] - 2.30011781356693*U[15]*U[3] - 1.21855791962175*U[15]*U[6] + 1.85472813238771*U[16]*U[5] - 1.85472813238771*U[17]*U[4] + 0.00900906225374311*U[1] + 0.00559477950653936*U[21]\n", - "\tF[12] = 1.43757363347933*U[0]*U[11] + 0.0122974647859652*U[0] - 1.02191932371371*U[10]*U[1] - 0.0122974647859652*U[10] - 0.0214771158470353*U[11] - 0.0315441157231782*U[12] - 1.63507091794193*U[13]*U[4] + 2.30011781356693*U[14]*U[3] - 1.21855791962175*U[14]*U[6] - 1.21855791962175*U[15]*U[7] + 1.85472813238771*U[16]*U[4] + 1.85472813238771*U[17]*U[5] + 0.00559477950653936*U[22] + 0.00900906225374311*U[2]\n", - "\tF[13] = -1.24843862507119*U[11]*U[5] + 1.24843862507119*U[12]*U[4] - 0.0363121306090808*U[13] - 2.3185288751322*U[14]*U[2] + 2.3185288751322*U[15]*U[1] + 0.017834837501017*U[15] - 2.49687725014237*U[16]*U[9] + 2.49687725014237*U[17]*U[8] - 4.63705775026441*U[18]*U[7] + 4.63705775026441*U[19]*U[6] + 0.00507126799557032*U[23] + 0.0121428571428571*U[3] - 0.017834837501017*U[5]\n", - "\tF[14] = -1.16886956037576*U[0]*U[15] + 0.422511733532696*U[10]*U[5] - 0.612715105162524*U[11]*U[7] - 1.38291034440645*U[12]*U[3] + 0.612715105162524*U[12]*U[6] + 1.79985224341047*U[13]*U[2] - 0.0412871146288803*U[14] + 0.0173705927405276*U[15] - 1.87294455066922*U[16]*U[2] + 1.87294455066922*U[17]*U[1] + 0.0124282982791587*U[17] + 0.00452503199094866*U[24] + 0.015412683237731*U[4] - 0.0124282982791587*U[7]\n", - "\tF[15] = 1.16886956037576*U[0]*U[14] - 0.422511733532696*U[10]*U[4] + 1.38291034440645*U[11]*U[3] + 0.612715105162524*U[11]*U[6] + 0.612715105162524*U[12]*U[7] - 1.79985224341047*U[13]*U[1] - 0.0159138129390846*U[13] - 0.0173705927405276*U[14] - 0.0412871146288803*U[15] - 1.87294455066922*U[16]*U[1] - 0.0124282982791587*U[16] - 1.87294455066922*U[17]*U[2] + 0.00452503199094866*U[25] + 0.0159138129390846*U[3] + 0.015412683237731*U[5] + 0.0124282982791587*U[6]\n", - "\tF[16] = -2.94535914360826*U[0]*U[17] + 0.569389237785845*U[10]*U[7] - 0.768581081081081*U[11]*U[5] - 0.768581081081081*U[12]*U[4] + 0.911022780457352*U[13]*U[9] + 1.42736486486486*U[14]*U[2] + 1.42736486486486*U[15]*U[1] + 0.0109797297297297*U[15] - 0.0460906434981493*U[16] + 0.0306919594705944*U[17] - 4.71257462977322*U[19]*U[3] + 0.00399762116767931*U[26] - 0.0109797297297297*U[5] + 0.0185698198198198*U[6]\n", - "\tF[17] = 2.94535914360826*U[0]*U[16] - 0.569389237785845*U[10]*U[6] + 0.768581081081081*U[11]*U[4] - 0.768581081081081*U[12]*U[5] - 0.911022780457352*U[13]*U[8] - 1.42736486486486*U[14]*U[1] - 0.0109797297297297*U[14] + 1.42736486486486*U[15]*U[2] - 0.0306919594705944*U[16] - 0.0460906434981493*U[17] + 4.71257462977322*U[18]*U[3] + 0.00399762116767931*U[27] + 0.0109797297297297*U[4] + 0.0185698198198198*U[7]\n", - "\tF[18] = -2.37660377951895*U[0]*U[19] + 0.0288656329496225*U[10]*U[9] + 1.50101291338039*U[13]*U[7] - 3.30992591155675*U[17]*U[3] - 0.0513521112007289*U[18] + 0.026256705211838*U[19] + 0.00341993024749443*U[28] + 0.0220279383429672*U[8]\n", - "\tF[19] = 2.37660377951895*U[0]*U[18] - 0.0288656329496225*U[10]*U[8] - 1.50101291338039*U[13]*U[6] + 3.30992591155675*U[16]*U[3] - 0.026256705211838*U[18] - 0.0513521112007289*U[19] + 0.00341993024749443*U[29] + 0.0220279383429672*U[9]\n", - "\tF[20] = 6.44421938347898e-6*C_go + 0.0172043158333333*U[10] - 0.00976477713178294*U[20]\n", - "\tF[21] = 0.0172043158333333*U[11] - 0.00976477713178294*U[21]\n", - "\tF[22] = 0.0172043158333333*U[12] - 0.00976477713178294*U[22]\n", - "\tF[23] = 0.0172043158333333*U[13] - 0.00976477713178294*U[23]\n", - "\tF[24] = 0.0172043158333333*U[14] - 0.00976477713178294*U[24]\n", - "\tF[25] = 0.0172043158333333*U[15] - 0.00976477713178294*U[25]\n", - "\tF[26] = 0.0172043158333333*U[16] - 0.00976477713178294*U[26]\n", - "\tF[27] = 0.0172043158333333*U[17] - 0.00976477713178294*U[27]\n", - "\tF[28] = 0.0172043158333333*U[18] - 0.00976477713178294*U[28]\n", - "\tF[29] = 0.0172043158333333*U[19] - 0.00976477713178294*U[29]\n", - "\treturn F" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7cededb7", - "metadata": {}, - "outputs": [], - "source": [ - "offset = 1 if model_parameters.dynamic_T else 0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b7e03156", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.ndim" - ] - }, - { - "cell_type": "markdown", - "id": "0579478b", - "metadata": {}, - "source": [ - "## Comparing with numerical results" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77155759", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f38b345f", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0f5941", - "metadata": {}, - "outputs": [], - "source": [ - "integrator = RungeKuttaIntegrator()\n", - "integrator.set_func(f)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "415ab585", - "metadata": {}, - "outputs": [], - "source": [ - "integrator_num = RungeKuttaIntegrator()\n", - "integrator_num.set_func(f_num)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e2b5f14a", - "metadata": {}, - "outputs": [], - "source": [ - "# ICs calculated from long transient\n", - "\n", - "ic = np.array([0.05055959, -0.01639403, -0.01440781, -0.01846523, -0.01352099,\n", - " 0.011685 , -0.00201673, -0.02030682, 0.03923588, -0.02229535,\n", - " 0.0586372 , -0.01805569, -0.01264252, -0.0103574 , -0.00618456,\n", - " 0.01159318, -0.00478694, -0.00782509, 0.01066059, -0.01552667,\n", - " 0.30718325, -0.03247899, -0.04512935, -0.00078786, -0.00067468,\n", - " 0.00183836, 0.00068025, 0.00215424, -0.00322845, -0.00186392])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c6d5773", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", - "reference_time, reference_traj = integrator.get_trajectories()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b38989ba", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator_num.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", - "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2f892676", - "metadata": {}, - "outputs": [], - "source": [ - "varx = 2\n", - "vary = 1\n", - "plt.figure(figsize=(12, 10))\n", - "\n", - "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", - "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", - "\n", - "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", - "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "68781088", - "metadata": {}, - "outputs": [], - "source": [ - "varx = 2\n", - "vary = 1\n", - "plt.figure(figsize=(12, 10))\n", - "\n", - "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", - "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", - "\n", - "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", - "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6697d107", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "t_s = 100000\n", - "\n", - "plt.plot(reference_time_num[:t_s] * model_parameters.dimensional_time, reference_traj_num[varx, :t_s])\n", - "plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "76ecb077", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "t_s = 1000\n", - "\n", - "vars = [1, 2]\n", - "\n", - "plt.plot(reference_traj_num[vars, :-1].T)\n", - "# plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", - "plt.show()" - ] - }, { "cell_type": "markdown", "id": "da46eccd", @@ -479,16 +145,6 @@ "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.gotemperature_params.C[0], model_parameters.atemperature_params.C[0], model_parameters.atmospheric_params.kd, model_parameters.atmospheric_params.kdp], language='auto')" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f2db3f4", - "metadata": {}, - "outputs": [], - "source": [ - "print(funcs[0].split('\\n')[208])" - ] - }, { "cell_type": "code", "execution_count": null, @@ -506,9 +162,10 @@ "metadata": {}, "outputs": [], "source": [ - "auto_eq_lines[34] = '\\tC_a1 = 0.4*C_go1'\n", - "auto_eq_lines[208] = '!'+auto_eq_lines[208]\n", - "auto_eq_lines[209] = '!'+auto_eq_lines[209]\n" + "for i, line in enumerate(auto_eq_lines):\n", + " if 'C_a1 = PAR(2)' in line:\n", + " auto_eq_lines[i] = '\\tC_a1 = 0.4*C_go1'\n", + " break" ] }, { @@ -538,17 +195,23 @@ "metadata": {}, "outputs": [], "source": [ - "auto_config_lines[-5] = \"UZR = {'C_go1': \" + str(list(np.arange(50.,375.,50.)))+\"}\"" + "for i, line in enumerate(auto_config_lines):\n", + " if 'UZR' in line:\n", + " auto_config_lines[i] = \"UZR = {'C_go1': \" + str(list(np.arange(50.,375.,50.)))+\"}\"\n", + " break" ] }, { "cell_type": "code", "execution_count": null, - "id": "cc781ecc", + "id": "cc7b79fe", "metadata": {}, "outputs": [], "source": [ - "auto_config_lines[-2] = \"UZSTOP = {'C_go1': [0.,400.]}\"" + "for i, line in enumerate(auto_config_lines):\n", + " if 'UZSTOP' in line:\n", + " auto_config_lines[i] = \"UZSTOP = {'C_go1': [0.,400.]}\"\n", + " break" ] }, { @@ -771,7 +434,7 @@ " fp = glob.glob(nb_dir + '/b.fp*')\n", " print(fp)\n", " for i in range(len(fp)-1,-1,-1):\n", - " if '~' in fp[i]:\n", + " if '~' in fp[i] or '_' in fp[i]:\n", " del fp[i]\n", " \n", " for i in range(len(fp)-1,-1,-1):\n", @@ -912,7 +575,8 @@ "metadata": {}, "outputs": [], "source": [ - "x=ac.run(mname, runner=runner)\n", + "U_dic = {i+1: 0. for i in range(model_parameters.ndim)}\n", + "x=ac.run(mname, U=U_dic, PAR={3: 0.085, 4: 0.02}, runner=runner)\n", "ac.save(x,'fp1')" ] }, @@ -1008,7 +672,7 @@ "execution_count": null, "id": "a2604fb2", "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ @@ -1021,21 +685,13 @@ "cell_type": "code", "execution_count": null, "id": "0593b6a8", - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ "plot_branches('./b.fp1_hp1')" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9f2918d4", - "metadata": {}, - "outputs": [], - "source": [ - "plot_branches('s.')" - ] } ], "metadata": { From 325c5b3437bf0e72343c0fbdacb7c9ea9b6482e8 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 20 Nov 2023 18:10:22 +0100 Subject: [PATCH 100/143] Corrected a bug in the base class for tests of symbolic outputs --- model_test/test_base_symbolic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model_test/test_base_symbolic.py b/model_test/test_base_symbolic.py index d35c1d4..732184d 100644 --- a/model_test/test_base_symbolic.py +++ b/model_test/test_base_symbolic.py @@ -54,7 +54,7 @@ def check_numerical_lists_flt(self): if len(self.symbolic_values) == 0: self.symbolic_outputs() self.numerical_outputs() - for v, r in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.reference)))): + for v, r in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.numerical_values)))): self.assertTrue(self.match_flt(v, r), msg=v+' != '+r+' !!!') def check_numerical_lists(self, cmax=1): From e624198f160c7b99bc9026d337da7089620ce809 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 20 Nov 2023 18:11:05 +0100 Subject: [PATCH 101/143] Corrected a bug in the symbol of the gamma_a parameter --- qgs/params/params.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qgs/params/params.py b/qgs/params/params.py index 0446013..b0034e9 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -1428,7 +1428,7 @@ def ground_basis(self, basis): self.atemperature_params.gamma = Parameter(1.e7, units='[J][m^-2][K^-1]', scale_object=self.scale_params, description='specific heat capacity of the atmosphere', - return_dimensional=True, symbol=Symbol('gamma_g')) + return_dimensional=True, symbol=Symbol('gamma_a')) if self.dynamic_T: self.atemperature_params.set_insolation((self.nmod[0] + 1) * [0.e0], None, True) self.atemperature_params.set_insolation(100.0, 0, True) @@ -1863,7 +1863,7 @@ def gblocks(self, value): scale_object=self.scale_params, description='specific heat capacity of the atmosphere', return_dimensional=True, - symbol=Symbol('gamma_g')) + symbol=Symbol('gamma_a')) self.atemperature_params.set_insolation(self.nmod[0] * [0.e0]) self.atemperature_params.set_insolation(100.0, 0) self.atemperature_params.eps = Parameter(0.76e0, input_dimensional=False, From e4edaef9994c706a9495a8936b8cdcaa98473d9f Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 20 Nov 2023 18:12:06 +0100 Subject: [PATCH 102/143] Added a test for the symbolic output of the land-atm model --- model_test/test_aotensor_sym_ground.py | 113 +++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 model_test/test_aotensor_sym_ground.py diff --git a/model_test/test_aotensor_sym_ground.py b/model_test/test_aotensor_sym_ground.py new file mode 100644 index 0000000..da9ae47 --- /dev/null +++ b/model_test/test_aotensor_sym_ground.py @@ -0,0 +1,113 @@ + +import sys +import os + +path = os.path.abspath('./') +base = os.path.basename(path) +if base == 'model_test': + sys.path.extend([os.path.abspath('../')]) +else: + sys.path.extend([path]) + + +import unittest +import numpy as np + +from qgs.params.params import QgParams +from qgs.inner_products import symbolic +from qgs.tensors.qgtensor import QgsTensor +from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor + +from model_test.test_base_symbolic import TestBaseSymbolic + +real_eps = np.finfo(np.float64).eps + + +class TestSymbolicAOTensor(TestBaseSymbolic): + ''' + Test class for the Linear Symbolic Tensor + The tensor is tested against the reference file, and then the numerical tensor calculated in qgs. + ''' + + filename = 'test_aotensor.ref' + + # def test_sym_against_ref(self): + # self.check_lists_flt() + + def test_sym_against_num(self): + self.check_numerical_lists_flt() + + def symbolic_outputs(self, output_func=None): + + if output_func is None: + self.symbolic_values.clear() + tfunc = self.save_ip_symbolic + else: + tfunc = output_func + + params = QgParams({'rr': 287.e0, 'sb': 5.6e-8}) + params.set_atmospheric_channel_fourier_modes(2, 2, mode="symbolic") + params.set_ground_channel_fourier_modes(2, 2, mode="symbolic") + + params.ground_params.set_orography(0.2, 1) + params.gotemperature_params.set_params({'gamma': 1.6e7, 'T0': 300}) + params.atemperature_params.set_params({'hlambda': 10, 'T0': 290}) + params.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.085, 'kdp': 0.02}) + C_g = 300 + params.atemperature_params.set_insolation(0.4 * C_g, 0) + params.gotemperature_params.set_insolation(C_g, 0) + + aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) + gip = symbolic.GroundSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) + + aip.connect_to_ground(gip, None) + + sym_aotensor = SymbolicQgsTensor(params=params, atmospheric_inner_products=aip, ground_inner_products=gip) + + subbed_tensor = sym_aotensor.sub_tensor() + + for coo, val in zip(subbed_tensor.keys(), subbed_tensor.values()): + _ip_string_format(tfunc, 'sym_aotensor', coo, val) + + def numerical_outputs(self, output_func=None): + + if output_func is None: + self.numerical_values.clear() + tfunc = self.save_ip_numeric + else: + tfunc = output_func + + params = QgParams({'rr': 287.e0, 'sb': 5.6e-8}) + params.set_atmospheric_channel_fourier_modes(2, 2, mode="symbolic") + params.set_ground_channel_fourier_modes(2, 2, mode="symbolic") + + params.ground_params.set_orography(0.2, 1) + params.gotemperature_params.set_params({'gamma': 1.6e7, 'T0': 300}) + params.atemperature_params.set_params({'hlambda': 10, 'T0': 290}) + params.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.085, 'kdp': 0.02}) + C_g = 300 + params.atemperature_params.set_insolation(0.4 * C_g, 0) + params.gotemperature_params.set_insolation(C_g, 0) + + aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=False) + gip = symbolic.GroundSymbolicInnerProducts(params, return_symbolic=False) + + aip.connect_to_ground(gip, None) + + num_aotensor = QgsTensor(params=params, atmospheric_inner_products=aip, ground_inner_products=gip) + + for coo, val in zip(num_aotensor.tensor.coords.T, num_aotensor.tensor.data): + _ip_string_format(tfunc, 'num_aotensor', coo, val) + + +def _ip_string_format(func, symbol, indices, value): + if abs(value) >= real_eps: + s = symbol + for i in indices: + s += "["+str(i)+"]" + s += " = % .5E" % value + func(s) + + +if __name__ == "__main__": + unittest.main() From 0b71693e950098ef838e42023f78dcc8c5c38fe2 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 20 Nov 2023 20:44:47 +0100 Subject: [PATCH 103/143] Cont'd --- model_test/test_aotensor_sym_ground.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model_test/test_aotensor_sym_ground.py b/model_test/test_aotensor_sym_ground.py index da9ae47..2ba2cbe 100644 --- a/model_test/test_aotensor_sym_ground.py +++ b/model_test/test_aotensor_sym_ground.py @@ -23,7 +23,7 @@ real_eps = np.finfo(np.float64).eps -class TestSymbolicAOTensor(TestBaseSymbolic): +class TestSymbolicGroundTensor(TestBaseSymbolic): ''' Test class for the Linear Symbolic Tensor The tensor is tested against the reference file, and then the numerical tensor calculated in qgs. From f064ccad6b324ea4be87971bf08c5b924ca9a547 Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 27 Nov 2023 10:15:29 +0100 Subject: [PATCH 104/143] symbolic dict can be provided not in equation order --- qgs/functions/symbolic_output.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 99a7e48..02366d4 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -357,15 +357,16 @@ def equation_as_function(equations, params, string_output=True, language='python for v in continuation_variables: f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") - for n, eq in enumerate(eq_dict.values()): - f_output.append('\tF['+str(n)+'] = ' + str(eq)) + for n, eq in eq_dict.items(): + f_output.append('\tF['+str(n-1)+'] = ' + str(eq)) f_output.append('\treturn F') f_output = '\n'.join(f_output) else: # Return a lambdified function vec = [Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] - array_eqs = np.array(list(eq_dict.values())) + sorted_dict = dict(sorted(eq_dict.items())) + array_eqs = np.array(list(sorted_dict.values())) inputs = ['t', vec] for v in continuation_variables: @@ -382,8 +383,8 @@ def equation_as_function(equations, params, string_output=True, language='python for v in continuation_variables: f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") - for n, eq in enumerate(eq_dict.values()): - f_output.append('\tdu['+str(n+1)+'] = ' + str(eq)) + for n, eq in eq_dict.items(): + f_output.append('\tdu['+str(n)+'] = ' + str(eq)) f_output.append('end') f_output = '\n'.join(f_output) @@ -426,7 +427,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('F = Array[' + str(len(eq_dict)) + ']') - for n, eq in enumerate(eq_dict.values()): + for n, eq in eq_dict.items(): f_output.append('F['+str(n+1)+'] = ' + str(eq)) # TODO !!!! Killing output as I have not tested the above code !!!! @@ -568,7 +569,7 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa def _split_equations(eq_dict, f_output, line_len=80): """Function to split FORTRAN equations to a set length when producing functions""" - for n, eq in enumerate(eq_dict.values()): + for n, eq in eq_dict.items(): # split equations to be a maximum of `line_len` # split remainder of equation into chunks of length `line_length` @@ -580,7 +581,7 @@ def _split_equations(eq_dict, f_output, line_len=80): f_output.append("\t\t&" + eq_chunks[-1]) else: - f_output.append('\tF(' + str(n + 1) + ') =\t ' + eq_chunks[0]) + f_output.append('\tF(' + str(n) + ') =\t ' + eq_chunks[0]) f_output.append('') return f_output From f4d650971e82200ff244e1c5d490b8542cd595f9 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 1 Dec 2023 15:14:36 +0100 Subject: [PATCH 105/143] Update of the Notebook --- .../Symbolic Output-Linear-ground-AUTO.ipynb | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb index 7c6f69a..b14c09a 100644 --- a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb +++ b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb @@ -432,10 +432,8 @@ " \n", " \n", " fp = glob.glob(nb_dir + '/b.fp*')\n", - " print(fp)\n", - " for i in range(len(fp)-1,-1,-1):\n", - " if '~' in fp[i] or '_' in fp[i]:\n", - " del fp[i]\n", + " fp = [item for item in fp if '~' not in item]\n", + " fp = [item for item in fp if '_' not in item]\n", " \n", " for i in range(len(fp)-1,-1,-1):\n", " \n", @@ -532,9 +530,7 @@ "outputs": [], "source": [ "lf=glob.glob(nb_dir + '/c.*')\n", - "for i in range(len(lf)-1,-1,-1):\n", - " if '~' in lf[i]:\n", - " del lf[i]\n", + "lf = [item for item in lf if '~' not in item]\n", "\n", "y=lf[0]\n", "mname=y[len(nb_dir)+3:]\n", @@ -572,11 +568,13 @@ "cell_type": "code", "execution_count": null, "id": "fe0c0600", - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [], "source": [ "U_dic = {i+1: 0. for i in range(model_parameters.ndim)}\n", - "x=ac.run(mname, U=U_dic, PAR={3: 0.085, 4: 0.02}, runner=runner)\n", + "x = ac.run(mname, U=U_dic, ICP=['C_go1'], PAR={3: model_parameters.atmospheric_params.kd, 4: model_parameters.atmospheric_params.kdp}, runner=runner)\n", "ac.save(x,'fp1')" ] }, @@ -587,7 +585,7 @@ "metadata": {}, "outputs": [], "source": [ - "plot_branch_vs_others(0)" + "plot_branch_vs_others(1)" ] }, { @@ -677,7 +675,7 @@ "outputs": [], "source": [ "s = solutions_list[0]\n", - "rh=ac.run(s,ICP=['C_go1', 'T'], IPS=2, NTST=400, NMX=0, runner=runner)\n", + "rh=ac.run(s,ICP=['C_go1', 'T'], IPS=2, NTST=400, NMX=258, runner=runner)\n", "ac.save(rh, 'fp1_hp1')" ] }, @@ -690,7 +688,7 @@ }, "outputs": [], "source": [ - "plot_branches('./b.fp1_hp1')" + "plot_branches('./b.fp1_hp1', variables=(0, 2))" ] } ], From b64e5748287c552738e77233ff9cd4f3c4aaf184 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 1 Dec 2023 16:05:57 +0100 Subject: [PATCH 106/143] Corrected a bug in commit https://gitlab-me.oma.be/ClimDyn/qgs/-/commit/f064ccad6b324ea4be87971bf08c5b924ca9a547 impacting the Fortran tendencies --- qgs/functions/symbolic_output.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index 02366d4..b3b3bad 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -428,7 +428,7 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('F = Array[' + str(len(eq_dict)) + ']') for n, eq in eq_dict.items(): - f_output.append('F['+str(n+1)+'] = ' + str(eq)) + f_output.append('F['+str(n+1)+'] = ' + str(eq)) # Jonathan: n+1 or n ?? Should be checked ! # TODO !!!! Killing output as I have not tested the above code !!!! f_output = '\n'.join(f_output) @@ -575,7 +575,7 @@ def _split_equations(eq_dict, f_output, line_len=80): # split remainder of equation into chunks of length `line_length` eq_chunks = [eq[x: x + line_len] for x in range(0, len(eq), line_len)] if len(eq_chunks) > 1: - f_output.append('\tF(' + str(n + 1) + ') =\t ' + eq_chunks[0] + "&") + f_output.append('\tF(' + str(n) + ') =\t ' + eq_chunks[0] + "&") for ln in eq_chunks[1:-1]: f_output.append("\t\t&" + ln + "&") From dd68ebb99c603b7b33d40556095e1bc33a18a0c2 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 1 Dec 2023 16:29:19 +0100 Subject: [PATCH 107/143] Changed the name of the AUTO variables (using var_string) Final change to notebook --- .../Symbolic Output-Linear-ground-AUTO.ipynb | 17 +++++++--- qgs/functions/symbolic_output.py | 34 +------------------ 2 files changed, 14 insertions(+), 37 deletions(-) diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb index b14c09a..0e24bca 100644 --- a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb +++ b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb @@ -662,7 +662,7 @@ "id": "91199f37", "metadata": {}, "source": [ - "### Computing the first Hopf bifurcation" + "### Computing the second Hopf bifurcation" ] }, { @@ -674,8 +674,8 @@ }, "outputs": [], "source": [ - "s = solutions_list[0]\n", - "rh=ac.run(s,ICP=['C_go1', 'T'], IPS=2, NTST=400, NMX=258, runner=runner)\n", + "s = solutions_list[1]\n", + "rh=ac.run(s,ICP=['C_go1', 'T'], IPS=2, NTST=400, runner=runner)\n", "ac.save(rh, 'fp1_hp1')" ] }, @@ -688,8 +688,17 @@ }, "outputs": [], "source": [ - "plot_branches('./b.fp1_hp1', variables=(0, 2))" + "ax, _ = plot_branches('./b.fp1_hp1', variables=(0, 3))\n", + "plot_branches('./b.fp1', ax=ax, variables=(0, 3))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "190b8b53", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index b3b3bad..d813d7c 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -544,7 +544,7 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa auto_config.append('parnames = ' + str(params_dic)) elif '! VARIABLES' in ln: - auto_config.append('unames = ' + str(_variable_names(params))) + auto_config.append('unames = ' + str({i+1: params.var_string[i] for i in range(params.ndim)})) elif '! DIMENSION' in ln: auto_config.append('NDIM = ' + str(params.ndim)) @@ -586,38 +586,6 @@ def _split_equations(eq_dict, f_output, line_len=80): return f_output -# Jonathan: why not use QgParams.var_string instead? -def _variable_names(params): - # Function to make the variable names for auto - num_v = params.number_of_variables - offset = 1 if params.number_of_variables else 0 - - var_list = list() - if params.atmospheric_basis is not None: - for i in range(num_v[0]): - var_list.append('psi' + str(i)) - - for i in range(offset, num_v[1]+offset): - var_list.append('theta' + str(i)) - - if params.ground_basis is not None: - for i in range(offset, num_v[2] + offset): - var_list.append('gT' + str(i)) - - if params.oceanic_basis is not None: - for i in range(num_v[2]): - var_list.append('A' + str(i)) - - for i in range(offset, num_v[3] + offset): - var_list.append('T' + str(i)) - - output = dict() - for i, v in enumerate(var_list): - output[i+1] = v - - return output - - # ------------- Default AUTO files templates ---------------- default_auto_main_template = """!---------------------------------------------------------------------- From 3277e90c68bf8b3220973fb40066ba2f7396b173 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sun, 7 Jan 2024 09:31:57 +0100 Subject: [PATCH 108/143] Update of the creation mechanisms for Params Array --- qgs/params/params.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qgs/params/params.py b/qgs/params/params.py index b0034e9..d896905 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -410,6 +410,7 @@ def _create_insolation(self, values, dynamic_T=False): if hasattr(values, "__iter__"): dim = len(values) + values = list(values) else: dim = values values = dim * [0.] @@ -453,6 +454,7 @@ def _create_thetas(self, values): if hasattr(values, "__iter__"): dim = len(values) + values = list(values) else: dim = values values = dim * [0.] @@ -577,6 +579,7 @@ def _create_insolation(self, values, dynamic_T=False): if hasattr(values, "__iter__"): dim = len(values) + values = list(values) else: dim = values values = dim * [0.] @@ -660,6 +663,7 @@ def _create_orography(self, values): if hasattr(values, "__iter__"): dim = len(values) + values = list(values) else: dim = values values = dim * [0.] @@ -744,6 +748,7 @@ def _create_insolation(self, values, dynamic_T=False): if hasattr(values, "__iter__"): dim = len(values) + values = list(values) else: dim = values values = dim * [0.] From db2a5ce3a701ccaaa9ef887ad36a5e7bf7c752b3 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 12 Jan 2024 12:19:59 +0100 Subject: [PATCH 109/143] Trying to symplify the parameters substitutions To be tested --- qgs/tensors/symbolic_qgtensor.py | 46 +++++++++++--------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 400e01a..01d3eb4 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -7,7 +7,7 @@ """ from qgs.functions.symbolic_mul import add_to_dict, symbolic_tensordot -from qgs.params.params import Parameter, ScalingParameter, ParametersArray +from qgs.params.params import Parameter, ScalingParameter, ParametersArray, Params import numpy as np from sympy import simplify @@ -800,9 +800,9 @@ def sub_tensor(self, tensor=None, continuation_variables=None): Parameters ---------- - tensor: dict or Sympy ImmutableSparseNDimArray + tensor: dict or ~sympy.tensor.array.ImmutableSparseNDimArray - continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) + continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray, ~sympy.core.symbol.Symbol) Variables which remain symbolic, all other variables are substituted with numerical values. If None all variables are substituted. @@ -842,8 +842,9 @@ def print_tensor(self, tensor=None, dict_opp=True, tol=1e-10): Parameters ---------- - tensor: dict or Sympy ImmutableSparseNDimArray - Tensor of model tendencies, either as a dictionary with keys of non-zero coordinates, and values of Sympy Symbols or floats, or as a ImmutableSparseNDimArray. + tensor: dict or ~sympy.tensor.array.ImmutableSparseNDimArray + Tensor of model tendencies, either as a dictionary with keys of non-zero coordinates, and values of ~sympy.core.symbol.Symbol or floats, or as a + ~sympy.tensor.array.ImmutableSparseNDimArray . dict_opp: bool ... tol: float @@ -1467,40 +1468,23 @@ def _parameter_substitutions(params, continuation_variables): subs = _parameter_values(params) - if 'scale_params' in params.__dict__.keys(): - subs.update(_parameter_values(params.scale_params)) + for obj in params.__dict__.keys(): + if isinstance(obj, Params): + subs.update(_parameter_values(obj)) - # Manually add properties from class - subs[params.scale_params.L.symbol] = params.scale_params.L - subs[params.scale_params.beta.symbol] = params.scale_params.beta - - if 'atmospheric_params' in params.__dict__.keys(): - if params.atmospheric_params is not None: - subs.update(_parameter_values(params.atmospheric_params)) - - if 'atemperature_params' in params.__dict__.keys(): - if params.atemperature_params is not None: - subs.update(_parameter_values(params.atemperature_params)) - - if 'oceanic_params' in params.__dict__.keys(): - if params.oceanic_params is not None: - subs.update(_parameter_values(params.oceanic_params)) - - if 'ground_params' in params.__dict__.keys(): - if params.ground_params is not None: - subs.update(_parameter_values(params.ground_params)) - - if 'gotemperature_params' in params.__dict__.keys(): - if params.gotemperature_params is not None: - subs.update(_parameter_values(params.gotemperature_params)) + # Manually add properties from class + subs[params.scale_params.L.symbol] = params.scale_params.L + subs[params.scale_params.beta.symbol] = params.scale_params.beta # Remove variables in continuation variables for cv in continuation_variables: if isinstance(cv, ParametersArray): for cv_i in cv.symbols: subs.pop(cv_i) - else: + elif hasattr(cv.symbol): subs.pop(cv.symbol) + else: + subs.pop(cv) return subs From d7d5532d2df7304d061422ea9668e6f06291309c Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 12 Jan 2024 12:24:59 +0100 Subject: [PATCH 110/143] Obvious bug corrected --- qgs/tensors/symbolic_qgtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 01d3eb4..59b2007 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -1481,7 +1481,7 @@ def _parameter_substitutions(params, continuation_variables): if isinstance(cv, ParametersArray): for cv_i in cv.symbols: subs.pop(cv_i) - elif hasattr(cv.symbol): + elif hasattr(cv, "symbol"): subs.pop(cv.symbol) else: subs.pop(cv) From 6b19b0f6b8e3eb03edb3b16a89a738248dae40cc Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 12 Jan 2024 12:39:29 +0100 Subject: [PATCH 111/143] Another bug --- qgs/tensors/symbolic_qgtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 59b2007..7f49810 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -1469,7 +1469,7 @@ def _parameter_substitutions(params, continuation_variables): subs = _parameter_values(params) for obj in params.__dict__.keys(): - if isinstance(obj, Params): + if issubclass(obj, Params): subs.update(_parameter_values(obj)) # Manually add properties from class From 2e9f816740a748b1ef5b6f9a03eb0560e2bff65d Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 12 Jan 2024 12:53:58 +0100 Subject: [PATCH 112/143] Trying again --- qgs/tensors/symbolic_qgtensor.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 7f49810..b227d18 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -1467,9 +1467,8 @@ def _shift_dict_keys(dic, shift): def _parameter_substitutions(params, continuation_variables): subs = _parameter_values(params) - - for obj in params.__dict__.keys(): - if issubclass(obj, Params): + for _, obj in params.__dict__.items(): + if issubclass(obj.__class__, Params): subs.update(_parameter_values(obj)) # Manually add properties from class From d4b6c7cfc04dd4fa613906f33c1377cf1ccddbdc Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 12 Jan 2024 13:21:51 +0100 Subject: [PATCH 113/143] Abandonning the idea of passing a list of symbols as continuation variables --- qgs/plotting/util.py | 2 +- qgs/tensors/atmo_thermo_tensor.py | 36 +++++++++++++------------- qgs/tensors/qgtensor.py | 36 +++++++++++++------------- qgs/tensors/symbolic_qgtensor.py | 42 +++++++++++++++---------------- 4 files changed, 58 insertions(+), 58 deletions(-) diff --git a/qgs/plotting/util.py b/qgs/plotting/util.py index 2eb522f..6d48620 100644 --- a/qgs/plotting/util.py +++ b/qgs/plotting/util.py @@ -21,7 +21,7 @@ def std_plot(x, mean, std, ax=None, **kwargs): std: ~numpy.ndarray 1D array of standard deviation values to represent, should have the same shape as mean. ax: None or ~matplotlib.axes.Axes - A `matplotlib`_ axes instance to plot the values. If None, create one. + A `matplotlib`_ axes instance to plot the values. If `None`, create one. kwargs: dict Keyword arguments to be given to the plot routine. diff --git a/qgs/tensors/atmo_thermo_tensor.py b/qgs/tensors/atmo_thermo_tensor.py index 1a740a7..2c9660b 100644 --- a/qgs/tensors/atmo_thermo_tensor.py +++ b/qgs/tensors/atmo_thermo_tensor.py @@ -26,13 +26,13 @@ class AtmoThermoTensor(QgsTensor): The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. Attributes ---------- @@ -40,13 +40,13 @@ class AtmoThermoTensor(QgsTensor): The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) @@ -233,13 +233,13 @@ class AtmoThermoTensorDynamicT(AtmoThermoTensor): The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. Attributes ---------- @@ -247,13 +247,13 @@ class AtmoThermoTensorDynamicT(AtmoThermoTensor): The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) @@ -455,13 +455,13 @@ class AtmoThermoTensorT4(AtmoThermoTensorDynamicT): The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. Attributes ---------- @@ -469,13 +469,13 @@ class AtmoThermoTensorT4(AtmoThermoTensorDynamicT): The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) diff --git a/qgs/tensors/qgtensor.py b/qgs/tensors/qgtensor.py index ee7177e..d4c2d38 100644 --- a/qgs/tensors/qgtensor.py +++ b/qgs/tensors/qgtensor.py @@ -25,13 +25,13 @@ class QgsTensor(object): The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. Attributes ---------- @@ -39,13 +39,13 @@ class QgsTensor(object): The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) @@ -849,13 +849,13 @@ class QgsTensorDynamicT(QgsTensor): The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. Attributes ---------- @@ -863,13 +863,13 @@ class QgsTensorDynamicT(QgsTensor): The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) @@ -1180,13 +1180,13 @@ class QgsTensorT4(QgsTensorDynamicT): The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. Attributes ---------- @@ -1194,13 +1194,13 @@ class QgsTensorT4(QgsTensorDynamicT): The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index b227d18..6a13e4b 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -29,15 +29,15 @@ class SymbolicQgsTensor(object): atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. The inner product is returned in symbolic or numeric form. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. The inner product is returned in symbolic or numeric form. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. The inner product is returned in symbolic or numeric form. Attributes @@ -47,13 +47,13 @@ class SymbolicQgsTensor(object): atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) @@ -802,9 +802,9 @@ def sub_tensor(self, tensor=None, continuation_variables=None): ---------- tensor: dict or ~sympy.tensor.array.ImmutableSparseNDimArray - continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray, ~sympy.core.symbol.Symbol) + continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) Variables which remain symbolic, all other variables are substituted with numerical values. - If None all variables are substituted. + If `None` all variables are substituted. Returns ------- @@ -889,13 +889,13 @@ class SymbolicQgsTensorDynamicT(SymbolicQgsTensor): The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. Attributes ---------- @@ -903,13 +903,13 @@ class SymbolicQgsTensorDynamicT(SymbolicQgsTensor): The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) @@ -1249,13 +1249,13 @@ class SymbolicQgsTensorT4(SymbolicQgsTensor): The models parameters to configure the tensor. `None` to initialize an empty tensor. Default to `None`. atmospheric_inner_products: None or AtmosphericInnerProducts, optional The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts, optional The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts, optional The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. Attributes ---------- @@ -1263,13 +1263,13 @@ class SymbolicQgsTensorT4(SymbolicQgsTensor): The models parameters used to configure the tensor. `None` for an empty tensor. atmospheric_inner_products: None or AtmosphericInnerProducts The inner products of the atmospheric basis functions on which the model's PDE atmospheric equations are projected. - If None, disable the atmospheric tendencies. Default to `None`. + If `None`, disable the atmospheric tendencies. Default to `None`. oceanic_inner_products: None or OceanicInnerProducts The inner products of the oceanic basis functions on which the model's PDE oceanic equations are projected. - If None, disable the oceanic tendencies. Default to `None`. + If `None`, disable the oceanic tendencies. Default to `None`. ground_inner_products: None or GroundInnerProducts The inner products of the ground basis functions on which the model's PDE ground equations are projected. - If None, disable the ground tendencies. Default to `None`. + If `None`, disable the ground tendencies. Default to `None`. tensor: sparse.COO(float) The tensor :math:`\\mathcal{T}_{i,j,k}` :math:`i`-th components. jacobian_tensor: sparse.COO(float) @@ -1482,7 +1482,7 @@ def _parameter_substitutions(params, continuation_variables): subs.pop(cv_i) elif hasattr(cv, "symbol"): subs.pop(cv.symbol) - else: + else: # Try ... who knows... subs.pop(cv) return subs From e3a56abb8c9e5c78f4836be799cc83208f5dba2b Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 12 Jan 2024 13:28:38 +0100 Subject: [PATCH 114/143] Commenting --- qgs/functions/symbolic_output.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index d813d7c..fb3a03c 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -56,7 +56,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con gnd_ip: SymbolicGroundInnerProducts, optional Allows for stored inner products to be input continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) - The variables to not substitute and to leave in the equations, if None no variables are substituted + The variables to not substitute and to leave in the equations, if `None` all variables are substituted. language: str Options for the output language syntax: 'python', 'julia', 'fortran', 'auto', 'mathematica'. Default to `python`. @@ -85,6 +85,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con """ make_ip_subs = True + # TODO: check this !!! if continuation_variables is None: make_ip_subs = False else: @@ -331,8 +332,8 @@ def equation_as_function(equations, params, string_output=True, language='python - `auto` - `mathematica` Default to `python`. - continuation_variables: ~sympy.sets.sets.Set(~sympy.core.symbol.Symbol) or list(~sympy.core.symbol.Symbol) or None - Variables that are not substituted with numerical values. If `None`, no symbols are substituted. + continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) + Variables that are not substituted with numerical values. If `None`, all symbols are substituted. Returns ------- @@ -449,7 +450,7 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa params: QgParams The parameters fully specifying the model configuration. continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) - Variables that are not substituted with numerical values. If None, no symbols are substituted + Variables that are not substituted with numerical values. If `None`, all symbols are substituted auto_main_template: str, optional The template to be used to generate the main AUTO file. If not provided, use the default template. From 8e8fa602e09c072eade50d96e183176c36f453b8 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sat, 13 Jan 2024 10:06:52 +0100 Subject: [PATCH 115/143] Orography spectral coefficients now have symbols Test are passing --- qgs/params/params.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qgs/params/params.py b/qgs/params/params.py index d896905..e79619b 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -651,7 +651,7 @@ def set_orography(self, value, pos=None, basis="atmospheric"): if isinstance(value, (float, int)) and pos is not None and self.hk is not None: self.hk[pos] = Parameter(value, scale_object=self._scale_params, description="spectral components "+str(pos+1)+" of the orography", - return_dimensional=False, input_dimensional=False, symbol=Symbol('hk')) + return_dimensional=False, input_dimensional=False, symbol=Symbol('hk_'+str(pos+1))) elif hasattr(value, "__iter__"): self._create_orography(value) else: @@ -669,9 +669,10 @@ def _create_orography(self, values): values = dim * [0.] d = ["spectral component "+str(pos+1)+" of the orography" for pos in range(dim)] + sy = [Symbol('hk_'+str(pos+1)) for pos in range(dim)] self.hk = ParametersArray(values, scale_object=self._scale_params, - description=d, return_dimensional=False, input_dimensional=False, symbols=values) + description=d, return_dimensional=False, input_dimensional=False, symbols=sy) class GroundTemperatureParams(Params): From 42e6da12e10401a160270f0c05ca72b367243f16 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sat, 13 Jan 2024 10:19:48 +0100 Subject: [PATCH 116/143] Cleaning and commenting --- qgs/functions/symbolic_output.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_output.py index fb3a03c..12ab945 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_output.py @@ -17,8 +17,6 @@ GroundSymbolicInnerProducts from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT, SymbolicQgsTensorT4 -import os - python_lang_translation = { 'sqrt': 'math.sqrt', 'pi': 'math.pi' @@ -59,7 +57,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con The variables to not substitute and to leave in the equations, if `None` all variables are substituted. language: str Options for the output language syntax: 'python', 'julia', 'fortran', 'auto', 'mathematica'. - Default to `python`. + Default to 'python'. return_inner_products: bool If `True`, return the inner products of the model. Default to `False`. return_jacobian: bool @@ -85,9 +83,8 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con """ make_ip_subs = True - # TODO: check this !!! if continuation_variables is None: - make_ip_subs = False + make_ip_subs = False # TODO: check this !!! For me it should be True because if no cv then all subs are done. else: for cv in continuation_variables: try: From b57d44654cf36cac64b58851cbf6e0cb37ef3cb1 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sat, 13 Jan 2024 18:30:29 +0100 Subject: [PATCH 117/143] Renamed symbolic_output to symbolic_tendencies to match tendencies module --- notebooks/Symbolic Output-Linear-ground test.ipynb | 2 +- notebooks/Symbolic Output-Linear-ground-AUTO.ipynb | 2 +- notebooks/Symbolic Output-Linear.ipynb | 2 +- notebooks/Symbolic Output-dynamic_T.ipynb | 4 ++-- qgs/functions/symbolic_mul.py | 8 +++++--- .../{symbolic_output.py => symbolic_tendencies.py} | 14 +++++++------- qgs/inner_products/analytic.py | 3 ++- qgs/inner_products/symbolic.py | 2 +- qgs/tensors/symbolic_qgtensor.py | 14 ++++++++++++++ 9 files changed, 34 insertions(+), 17 deletions(-) rename qgs/functions/{symbolic_output.py => symbolic_tendencies.py} (98%) diff --git a/notebooks/Symbolic Output-Linear-ground test.ipynb b/notebooks/Symbolic Output-Linear-ground test.ipynb index 5fd0f41..4cfac4d 100644 --- a/notebooks/Symbolic Output-Linear-ground test.ipynb +++ b/notebooks/Symbolic Output-Linear-ground test.ipynb @@ -160,7 +160,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function, format_equations" + "from qgs.functions.symbolic_tendencies import create_symbolic_equations, translate_equations, equation_as_function, format_equations" ] }, { diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb index 0e24bca..5a5e48a 100644 --- a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb +++ b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb @@ -69,7 +69,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations" + "from qgs.functions.symbolic_tendencies import create_symbolic_equations" ] }, { diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb index f265020..050197c 100644 --- a/notebooks/Symbolic Output-Linear.ipynb +++ b/notebooks/Symbolic Output-Linear.ipynb @@ -134,7 +134,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, create_auto_file, default_auto_c_template" + "from qgs.functions.symbolic_tendencies import create_symbolic_equations, create_auto_file, default_auto_c_template" ] }, { diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index bb8a6ed..feef442 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -123,7 +123,7 @@ "metadata": {}, "outputs": [], "source": [ - "from qgs.functions.symbolic_output import create_symbolic_equations, translate_equations, equation_as_function" + "from qgs.functions.symbolic_tendencies import create_symbolic_equations, translate_equations, equation_as_function" ] }, { @@ -181,7 +181,7 @@ "outputs": [], "source": [ "from qgs.functions.symbolic_mul import symbolic_sparse_mult5\n", - "from qgs.functions.symbolic_output import equation_as_function" + "from qgs.functions.symbolic_tendencies import equation_as_function" ] }, { diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py index 60d93f3..ca45d0b 100644 --- a/qgs/functions/symbolic_mul.py +++ b/qgs/functions/symbolic_mul.py @@ -27,12 +27,12 @@ def add_to_dict(dic, loc, value): def symbolic_tensordot(a, b, axes=2): """Compute tensor dot product along specified axes of two sympy symbolic arrays - This is based on ~numpy.tensordot . + This is based on `Numpy`_ :meth:`~numpy.tensordot` . Parameters ---------- - a, b: Sympy arrays or tensors - Tensors to take the dot product of. + a, b: ~sympy.tensor.array.DenseNDimArray or ~sympy.tensor.array.SparseNDimArray + Arrays to take the dot product of. axes: int Sum over the last `axes` axes of `a` and the first `axes` axes @@ -43,6 +43,8 @@ def symbolic_tensordot(a, b, axes=2): output: sympy tensor The tensor dot product of the input. + .. _Numpy: https://numpy.org/ + """ as_ = a.shape nda = len(as_) diff --git a/qgs/functions/symbolic_output.py b/qgs/functions/symbolic_tendencies.py similarity index 98% rename from qgs/functions/symbolic_output.py rename to qgs/functions/symbolic_tendencies.py index 12ab945..3ae7488 100644 --- a/qgs/functions/symbolic_output.py +++ b/qgs/functions/symbolic_tendencies.py @@ -1,6 +1,6 @@ """ - Symbolic output module - ====================== + Symbolic tendencies module + ========================== This module provides functions to create a symbolic representation of the tendencies functions of the model in various languages and for various external software. @@ -48,11 +48,11 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con params: QgParams The parameters fully specifying the model configuration. atm_ip: SymbolicAtmosphericInnerProducts, optional - Allows for stored inner products to be input + Allows for stored inner products to be input. ocn_ip: SymbolicOceanInnerProducts, optional - Allows for stored inner products to be input + Allows for stored inner products to be input. gnd_ip: SymbolicGroundInnerProducts, optional - Allows for stored inner products to be input + Allows for stored inner products to be input. continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) The variables to not substitute and to leave in the equations, if `None` all variables are substituted. language: str @@ -63,7 +63,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con return_jacobian: bool If `True`, return the Jacobian of the model. Default to `False`. return_symbolic_eqs: bool - If `True`, return the substituted symbolic equations + If `True`, return the substituted symbolic equations. return_symbolic_qgtensor: bool If `True`, return the symbolic tendencies tensor of the model. Default to `False`. @@ -95,7 +95,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con if not make_ip_subs: warnings.warn("Calculating inner products symbolically, as the variable 'n' has been specified as a variable, " - "this takes several minutes.") + "this may take a while.") if params.atmospheric_basis is not None: if atm_ip is None: diff --git a/qgs/inner_products/analytic.py b/qgs/inner_products/analytic.py index 036c483..fa40f6b 100644 --- a/qgs/inner_products/analytic.py +++ b/qgs/inner_products/analytic.py @@ -41,7 +41,8 @@ from qgs.basis.fourier import channel_wavenumbers, basin_wavenumbers from qgs.inner_products.base import AtmosphericInnerProducts, OceanicInnerProducts, GroundInnerProducts -# TODO: Add warnings if trying to connect analytic and symbolic inner products together +# TODO: - Add warnings if trying to connect analytic and symbolic inner products together +# - Allow analytic inner product to be returned as symbolic arrays class AtmosphericAnalyticInnerProducts(AtmosphericInnerProducts): diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index 7b2ed62..a6e79ff 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -47,7 +47,7 @@ class AtmosphericSymbolicInnerProducts(AtmosphericInnerProducts): An instance of model's parameters object or a list in the form [aspect_ratio, atmospheric_basis, basis, oog, oro_basis]. If a list is provided, `aspect_ratio` is the aspect ratio of the domain, `atmospheric_basis` is a SymbolicBasis with the modes of the atmosphere, and `ocean_basis` is either `None` or a SymbolicBasis object with the modes of - the ocean or the ground. Finally `oog` indicates if it is an ocean or a ground component that is connected, + the ocean or the ground. Finally, `oog` indicates if it is an ocean or a ground component that is connected, by setting it to `ocean` or to 'ground', and in this latter case, `oro_basis` indicates on which basis the orography is developed. stored: bool, optional Indicate if the inner product must be stored or computed on the fly. Default to `True` diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 6a13e4b..23eb593 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -1513,3 +1513,17 @@ def _parameter_values(pars): dic = add_to_dict(dic, (0, 0), 1) dic = add_to_dict(dic, (0, 0), 2) print(dic) + + from qgs.params.params import QgParams + from qgs.inner_products import symbolic + + params = QgParams({'rr': 287.e0, 'sb': 5.6e-8}) + params.set_atmospheric_channel_fourier_modes(6, 6, mode="symbolic") + params.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.1, 'kdp': 0.01}) + + params.ground_params.set_orography(0.2, 1) + params.atemperature_params.set_thetas(0.1, 0) + + aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) + + # sym_aotensor = SymbolicQgsTensor(params=params, atmospheric_inner_products=aip) From ff9cdb0ffcb878e8322634ab9611b6bc7756b6e4 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sun, 14 Jan 2024 11:37:45 +0100 Subject: [PATCH 118/143] Cleaning --- qgs/inner_products/symbolic.py | 52 +++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index a6e79ff..aea9a96 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -32,7 +32,7 @@ from qgs.inner_products.base import AtmosphericInnerProducts, OceanicInnerProducts, GroundInnerProducts from qgs.inner_products.definition import StandardSymbolicInnerProductDefinition from scipy.integrate import dblquad -import sympy as sy +from sympy import ImmutableSparseMatrix, ImmutableSparseNDimArray # TODO: - Add warnings if trying to connect analytic and symbolic inner products together @@ -265,7 +265,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._d, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._d = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, noc, output) + self._d = ImmutableSparseMatrix(self.natm, noc, output) else: self._d = self._d.to_coo() @@ -275,7 +275,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._s, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._s = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, noc, output) + self._s = ImmutableSparseMatrix(self.natm, noc, output) else: self._s = self._s.to_coo() @@ -294,7 +294,7 @@ def connect_to_ocean(self, ocean_basis, num_threads=None, timeout=None): if self._T4 or self._dynamic_T: if self.return_symbolic: - self._v = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, noc, noc, noc, noc)) + self._v = ImmutableSparseNDimArray(output, shape=(self.natm, noc, noc, noc, noc)) else: self._v = self._v.to_coo() @@ -359,7 +359,7 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti output = _parallel_compute(pool, args_list, subs, self._s, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._s = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, ngr, output) + self._s = ImmutableSparseMatrix(self.natm, ngr, output) else: self._s = self._s.to_coo() @@ -370,7 +370,7 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti output = _parallel_compute(pool, args_list, subs, self._gh, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._gh = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, ngr)) + self._gh = ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, ngr)) else: if self._gh is not None: self._gh = self._gh.to_coo() @@ -390,7 +390,7 @@ def connect_to_ground(self, ground_basis, orographic_basis, num_threads=None, ti if self._T4 or self._dynamic_T: if self.return_symbolic: - self._v = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, ngr, ngr, ngr, ngr)) + self._v = ImmutableSparseNDimArray(output, shape=(self.natm, ngr, ngr, ngr, ngr)) else: self._v = self._v.to_coo() @@ -440,7 +440,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._a, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._a = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) + self._a = ImmutableSparseMatrix(self.natm, self.natm, output) else: self._a = self._a.to_coo() @@ -450,7 +450,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._u, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._u = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) + self._u = ImmutableSparseMatrix(self.natm, self.natm, output) else: self._u = self._u.to_coo() @@ -460,7 +460,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._c, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._c = sy.matrices.immutable.ImmutableSparseMatrix(self.natm, self.natm, output) + self._c = ImmutableSparseMatrix(self.natm, self.natm, output) else: self._c = self._c.to_coo() @@ -470,7 +470,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._b, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._b = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) + self._b = ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) else: self._b = self._b.to_coo() @@ -480,7 +480,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._g, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._g = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) + self._g = ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm)) else: self._g = self._g.to_coo() @@ -499,7 +499,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): if self._T4 or self._dynamic_T: if self.return_symbolic: - self._z = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm, self.natm, self.natm)) + self._z = ImmutableSparseNDimArray(output, shape=(self.natm, self.natm, self.natm, self.natm, self.natm)) else: self._z = self._z.to_coo() @@ -895,7 +895,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None output = _parallel_compute(pool, args_list, subs, self._K, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._K = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, natm, output) + self._K = ImmutableSparseMatrix(self.noc, natm, output) else: self._K = self._K.to_coo() @@ -906,7 +906,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None output = _parallel_compute(pool, args_list, subs, self._W, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._W = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, natm, output) + self._W = ImmutableSparseMatrix(self.noc, natm, output) else: self._W = self._W.to_coo() @@ -925,7 +925,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None if self._T4 or self._dynamic_T: if self.return_symbolic: - self._Z = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, natm, natm, natm, natm)) + self._Z = ImmutableSparseNDimArray(output, shape=(self.noc, natm, natm, natm, natm)) else: self._Z = self._Z.to_coo() @@ -974,7 +974,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._N, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._N = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + self._N = ImmutableSparseMatrix(self.noc, self.noc, output) else: self._N = self._N.to_coo() @@ -983,7 +983,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._M, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._M = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + self._M = ImmutableSparseMatrix(self.noc, self.noc, output) else: self._M = self._M.to_coo() @@ -992,7 +992,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._U, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._U = sy.matrices.immutable.ImmutableSparseMatrix(self.noc, self.noc, output) + self._U = ImmutableSparseMatrix(self.noc, self.noc, output) else: self._U = self._U.to_coo() @@ -1002,7 +1002,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._O, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._O = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) + self._O = ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) else: self._O = self._O.to_coo() @@ -1012,7 +1012,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): output = _parallel_compute(pool, args_list, subs, self._C, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._C = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) + self._C = ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc)) else: self._C = self._C.to_coo() @@ -1031,7 +1031,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): if self._T4 or self._dynamic_T: if self.return_symbolic: - self._V = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc, self.noc, self.noc)) + self._V = ImmutableSparseNDimArray(output, shape=(self.noc, self.noc, self.noc, self.noc, self.noc)) else: self._V = self._V.to_coo() @@ -1373,7 +1373,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None for j in range(natm)] output = _parallel_compute(pool, args_list, subs, self._W, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._W = sy.matrices.immutable.ImmutableSparseMatrix(self.ngr, natm, output) + self._W = ImmutableSparseMatrix(self.ngr, natm, output) else: self._W = self._W.to_coo() @@ -1392,7 +1392,7 @@ def connect_to_atmosphere(self, atmosphere_basis, num_threads=None, timeout=None if self._T4 or self._dynamic_T: if self.return_symbolic: - self._Z = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.ngr, natm, natm, natm, natm)) + self._Z = ImmutableSparseNDimArray(output, shape=(self.ngr, natm, natm, natm, natm)) else: self._Z = self._Z.to_coo() @@ -1432,7 +1432,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): args_list = [[(i, j), self.ip.symbolic_inner_product, (self._phi(i), self._phi(j))] for i in range(self.ngr) for j in range(self.ngr)] output = _parallel_compute(pool, args_list, subs, self._U, timeout, symbolic_int=not self.mk_subs) if self.return_symbolic: - self._U = sy.matrices.immutable.ImmutableSparseMatrix(self.ngr, self.ngr, output) + self._U = ImmutableSparseMatrix(self.ngr, self.ngr, output) else: self._U = self._U.to_coo() @@ -1452,7 +1452,7 @@ def compute_inner_products(self, num_threads=None, timeout=None): if self._T4 or self._dynamic_T: if self.return_symbolic: - self._V = sy.tensor.array.ImmutableSparseNDimArray(output, shape=(self.ngr, self.ngr, self.ngr, self.ngr, self.ngr)) + self._V = ImmutableSparseNDimArray(output, shape=(self.ngr, self.ngr, self.ngr, self.ngr, self.ngr)) else: self._V = self._V.to_coo() From 0435b242b862366e516979a8c75720f845802cea Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Sun, 14 Jan 2024 11:46:09 +0100 Subject: [PATCH 119/143] Cont'd --- qgs/inner_products/symbolic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qgs/inner_products/symbolic.py b/qgs/inner_products/symbolic.py index aea9a96..ccc6dd9 100644 --- a/qgs/inner_products/symbolic.py +++ b/qgs/inner_products/symbolic.py @@ -32,7 +32,7 @@ from qgs.inner_products.base import AtmosphericInnerProducts, OceanicInnerProducts, GroundInnerProducts from qgs.inner_products.definition import StandardSymbolicInnerProductDefinition from scipy.integrate import dblquad -from sympy import ImmutableSparseMatrix, ImmutableSparseNDimArray +from sympy import ImmutableSparseMatrix, ImmutableSparseNDimArray, lambdify # TODO: - Add warnings if trying to connect analytic and symbolic inner products together @@ -1590,7 +1590,7 @@ def _num_apply(ls): else: num_integrand = integrand[0] - func = sy.lambdify((integrand[1][0], integrand[2][0]), num_integrand, 'numpy') + func = lambdify((integrand[1][0], integrand[2][0]), num_integrand, 'numpy') try: a = integrand[2][1].subs(ls[3]) From e364c404b22ab84fc862fe9f2be2e27c49dff9b7 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Mon, 15 Jan 2024 14:22:26 +0100 Subject: [PATCH 120/143] Trying to accelerate a bit the symbolic equations Test passing --- qgs/tensors/symbolic_qgtensor.py | 48 +++++++++++++++++--------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 23eb593..767fd5f 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -245,17 +245,17 @@ def _compute_tensor_dicts(self): if aips.stored and go: # psi_a part - a_inv_mult_c = symbolic_tensordot(a_inv, aips._c[offset:, offset:], axes=1) + a_inv_mult_c = a_inv @ aips._c[offset:, offset:] if gp is not None: - hk_sym_arr = ImmutableSparseNDimArray(gp.hk.symbols) + hk_sym_arr = ImmutableSparseMatrix(gp.hk.symbols) if gp.orographic_basis == "atmospheric": a_inv_mult_g = symbolic_tensordot(a_inv, aips._g[offset:, offset:, offset:], axes=1) - oro = symbolic_tensordot(a_inv_mult_g, hk_sym_arr, axes=1) + oro = symbolic_tensordot(a_inv_mult_g, hk_sym_arr, axes=1)[:, :, 0] else: a_inv_mult_gh = symbolic_tensordot(a_inv, aips._gh[offset:, offset:, offset:], axes=1) - oro = symbolic_tensordot(a_inv_mult_gh, hk_sym_arr, axes=1) + oro = symbolic_tensordot(a_inv_mult_gh, hk_sym_arr, axes=1)[:, :, 0] a_inv_mult_b = symbolic_tensordot(a_inv, aips._b[offset:, offset:, offset:], axes=1) @@ -285,34 +285,34 @@ def _compute_tensor_dicts(self): sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_a(i), self._psi_o(j), 0), a_inv_mult_d[i, j] * ap.kd.symbol / 2) # theta_a part - a_theta_mult_u = symbolic_tensordot(a_theta, aips._u, axes=1) + a_theta_mult_u = a_theta @ aips._u if self.params.Cpa is not None: - val_Cpa = symbolic_tensordot(a_theta_mult_u, ImmutableSparseNDimArray(self.params.Cpa.symbolic_expressions), axes=1) + val_Cpa = a_theta_mult_u @ ImmutableSparseMatrix(self.params.Cpa.symbolic_expressions) if atp.hd is not None and atp.thetas is not None: - thetas_sym_arr = ImmutableSparseNDimArray(atp.thetas.symbols) - val_thetas = symbolic_tensordot(a_theta_mult_u, thetas_sym_arr, axes=1) # not perfect + thetas_sym_arr = ImmutableSparseMatrix(atp.thetas.symbols) + val_thetas = a_theta_mult_u @ thetas_sym_arr + + a_theta_mult_a = a_theta @ aips._a[:, offset:] + a_theta_mult_c = a_theta @ aips._c[:, offset:] - a_theta_mult_a = symbolic_tensordot(a_theta, aips._a[:, offset:], axes=1) - a_theta_mult_c = symbolic_tensordot(a_theta, aips._c[:, offset:], axes=1) - a_theta_mult_g = symbolic_tensordot(a_theta, aips._g[:, offset:, offset:], axes=1) if gp is not None: if gp.orographic_basis == "atmospheric": - oro = symbolic_tensordot(a_theta_mult_g, hk_sym_arr, axes=1) + oro = symbolic_tensordot(a_theta_mult_g, hk_sym_arr, axes=1)[:, :, 0] else: a_theta_mult_gh = symbolic_tensordot(a_theta, aips._gh[:, offset:, offset:], axes=1) - oro = symbolic_tensordot(a_theta_mult_gh, hk_sym_arr, axes=1) + oro = symbolic_tensordot(a_theta_mult_gh, hk_sym_arr, axes=1)[:, :, 0] a_theta_mult_b = symbolic_tensordot(a_theta, aips._b[:, offset:, offset:], axes=1) if ocean: - a_theta_mult_d = symbolic_tensordot(a_theta, aips._d[:, offset:], axes=1) - a_theta_mult_s = symbolic_tensordot(a_theta, aips._s, axes=1) + a_theta_mult_d = a_theta @ aips._d[:, offset:] + a_theta_mult_s = a_theta @ aips._s if ground_temp: - a_theta_mult_s = symbolic_tensordot(a_theta, aips._s, axes=1) + a_theta_mult_s = a_theta @ aips._s for i in range(nvar[1]): if self.params.Cpa is not None: @@ -372,9 +372,9 @@ def _compute_tensor_dicts(self): if ocean: # psi_o part - M_psio_mult_K = symbolic_tensordot(M_psio, bips._K[offset:, offset:], axes=1) - M_psio_mult_N = symbolic_tensordot(M_psio, bips._N[offset:, offset:], axes=1) - M_psio_mult_M = symbolic_tensordot(M_psio, bips._M[offset:, offset:], axes=1) + M_psio_mult_K = M_psio @ bips._K[offset:, offset:] + M_psio_mult_N = M_psio @ bips._N[offset:, offset:] + M_psio_mult_M = M_psio @ bips._M[offset:, offset:] M_psio_mult_C = symbolic_tensordot(M_psio, bips._C[offset:, offset:, offset:], axes=1) for i in range(nvar[2]): @@ -393,8 +393,9 @@ def _compute_tensor_dicts(self): sy_arr_dic = add_to_dict(sy_arr_dic, (self._psi_o(i), self._psi_o(j), self._psi_o(k)), - M_psio_mult_C[i, j, k]) # deltaT_o part - U_inv_mult_W = symbolic_tensordot(U_inv, bips._W, axes=1) - U_inv_mult_W_Cpgo = symbolic_tensordot(U_inv_mult_W, ImmutableSparseNDimArray(self.params.Cpgo.symbolic_expressions), axes=1) + U_inv_mult_W = U_inv @ bips._W + Cpgo_sym_arr = ImmutableSparseMatrix(self.params.Cpgo.symbolic_expressions) + U_inv_mult_W_Cpgo = U_inv_mult_W @ Cpgo_sym_arr U_inv_mult_O = symbolic_tensordot(U_inv, bips._O[:, offset:, offset:], axes=1) @@ -418,8 +419,9 @@ def _compute_tensor_dicts(self): # deltaT_g part if ground_temp: - U_inv_mult_W = symbolic_tensordot(U_inv, bips._W, axes=1) - U_inv_mult_W_Cpgo = symbolic_tensordot(U_inv_mult_W, ImmutableSparseNDimArray(self.params.Cpgo.symbolic_expressions), axes=1) + U_inv_mult_W = U_inv @ bips._W + Cpgo_sym_arr = ImmutableSparseMatrix(self.params.Cpgo.symbolic_expressions) + U_inv_mult_W_Cpgo = U_inv_mult_W @ Cpgo_sym_arr for i in range(nvar[2]): sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), 0, 0), U_inv_mult_W_Cpgo[i]) From b31a353073348eae3ada72d7ec72a00a655276cb Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Fri, 26 Jan 2024 20:07:39 +0100 Subject: [PATCH 121/143] Fixed a bug in the plot functions of AUTO notebook --- notebooks/Symbolic Output-Linear-ground-AUTO.ipynb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb index 5a5e48a..0bf2859 100644 --- a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb +++ b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb @@ -432,8 +432,8 @@ " \n", " \n", " fp = glob.glob(nb_dir + '/b.fp*')\n", - " fp = [item for item in fp if '~' not in item]\n", - " fp = [item for item in fp if '_' not in item]\n", + " fp = [item for item in fp if '~' not in os.path.basename(item)]\n", + " fp = [item for item in fp if '_' not in os.path.basename(item)]\n", " \n", " for i in range(len(fp)-1,-1,-1):\n", " \n", @@ -569,7 +569,7 @@ "execution_count": null, "id": "fe0c0600", "metadata": { - "scrolled": true + "scrolled": false }, "outputs": [], "source": [ From ab4fd635a42d4a202d2f36c183a34a5be5c030c3 Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 15 Feb 2024 17:13:35 +0100 Subject: [PATCH 122/143] Output of jacobian as function - ERROR IN JACOBIAN --- qgs/functions/symbolic_tendencies.py | 136 +++++++++++++++++++++++++-- 1 file changed, 130 insertions(+), 6 deletions(-) diff --git a/qgs/functions/symbolic_tendencies.py b/qgs/functions/symbolic_tendencies.py index 3ae7488..585a27c 100644 --- a/qgs/functions/symbolic_tendencies.py +++ b/qgs/functions/symbolic_tendencies.py @@ -174,6 +174,10 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con func = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, continuation_variables=continuation_variables) + if return_jacobian: + func_jac = jacobian_as_function(equations=dict_eq_simplified, params=params, language=language, + continuation_variables=continuation_variables) + ret = list() ret.append(func) if return_jacobian: @@ -287,8 +291,11 @@ def format_equations(equations, params, save_loc=None, language='python', print_ vector_subs['U_'+str(i)] = Symbol('U('+str(i)+')') for k in equations.keys(): - eq = equations[k].subs(vector_subs) - eq = eq.evalf() + if isinstance(equations[k], float): + eq = equations[k] + else: + eq = equations[k].subs(vector_subs) + eq = eq.evalf() if (language is not None) and print_equations: eq = translate_equations(eq, language) @@ -426,7 +433,118 @@ def equation_as_function(equations, params, string_output=True, language='python f_output.append('F = Array[' + str(len(eq_dict)) + ']') for n, eq in eq_dict.items(): - f_output.append('F['+str(n+1)+'] = ' + str(eq)) # Jonathan: n+1 or n ?? Should be checked ! + f_output.append('F['+str(n)+'] = ' + str(eq)) + + # TODO !!!! Killing output as I have not tested the above code !!!! + f_output = '\n'.join(f_output) + f_output = None + + return f_output + + +def jacobian_as_function(equations, params, language='python', continuation_variables=None): + """Converts the symbolic equations of the jacobain to a function in string format in the language syntax specified, + or a lambdified python function. + + Parameters + ---------- + equations: dict(~sympy.core.expr.Expr) + Dictionary of the substituted symbolic model equations. + params: QgParams + The parameters fully specifying the model configuration. + language: str + Language syntax that the equations are returned in. Options are: + - `python` + - `fortran` + - `julia` + - `auto` + - `mathematica` + Default to `python`. + continuation_variables: Iterable(Parameter, ScalingParameter, ParametersArray) + Variables that are not substituted with numerical values. If `None`, all symbols are substituted. + + Returns + ------- + f_output: callable or str + If string_output is `True`, output is a function in the specified language syntax, if `False` the output is + a lambdified python function. + + """ + if continuation_variables is None: + continuation_variables = list() + + eq_dict = format_equations(equations, params, language=language) + + f_output = list() + if language == 'python': + f_output.append('def jac(t, U, **kwargs):') + f_output.append('\t#Jacobian function of the qgs model') + f_output.append('\tJ = np.zeros((len(U), len(U)))') + + for v in continuation_variables: + f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") + + for n, eq in eq_dict.items(): + f_output.append('\tJ[' + str(n[0] - 1) + ', ' + str(n[1] - 1) + '] = ' + str(eq)) + + f_output.append('\treturn J') + f_output = '\n'.join(f_output) + + if language == 'julia': + eq_dict = translate_equations(eq_dict, language='julia') + + f_output.append('function jac!(du, U, p, t)') + f_output.append('\t#Jacobian function of the qgs model') + + for v in continuation_variables: + f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") + + for n, eq in eq_dict.items(): + f_output.append('\tdu[' + str(n[0]) + ', ' + str(n[1]) + '] = ' + str(eq)) + + f_output.append('end') + f_output = '\n'.join(f_output) + + if language == 'fortran': + eq_dict = translate_equations(eq_dict, language='fortran') + + f_var = '' + if len(continuation_variables) > 0: + for fv in continuation_variables: + f_var += str(fv.symbol) + ', ' + f_output.append('SUBROUTINE FUNC(NDIM, t, U, JAC, ' + f_var[:-2] + ')') + else: + f_output.append('SUBROUTINE FUNC(NDIM, t, U, JAC)') + + f_output.append('\t!Jacobian function of the qgs model') + f_output.append('\tINTEGER, INTENT(IN) :: NDIM') + f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*)') + f_output.append('\tDOUBLE PRECISION, INTENT(OUT) :: JAC(NDIM)') + + for v in continuation_variables: + f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: ' + str(v.symbol)) + + f_output.append('') + + f_output = _split_equations(eq_dict, f_output) + + f_output.append('END SUBROUTINE') + f_output = '\n'.join(f_output) + + if language == 'auto': + eq_dict = translate_equations(eq_dict, language='fortran') + + eq_dict = _split_equations(eq_dict, f_output, two_dim=True) + f_output = create_auto_file(eq_dict, params, continuation_variables) + + if language == 'mathematica': + # TODO: This function needs testing before release + eq_dict = translate_equations(eq_dict, language='mathematica') + + f_output.append('jac = Array[' + str(len(eq_dict)) + ']') + + for n, eq in eq_dict.items(): + f_output.append('jac[' + str(n[0]) + ', ' + str(n[1]) + '] = ' + str(eq)) # TODO !!!! Killing output as I have not tested the above code !!!! f_output = '\n'.join(f_output) @@ -564,7 +682,7 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa return '\n'.join(auto_file), '\n'.join(auto_config) -def _split_equations(eq_dict, f_output, line_len=80): +def _split_equations(eq_dict, f_output, line_len=80, two_dim=False): """Function to split FORTRAN equations to a set length when producing functions""" for n, eq in eq_dict.items(): @@ -573,13 +691,19 @@ def _split_equations(eq_dict, f_output, line_len=80): # split remainder of equation into chunks of length `line_length` eq_chunks = [eq[x: x + line_len] for x in range(0, len(eq), line_len)] if len(eq_chunks) > 1: - f_output.append('\tF(' + str(n) + ') =\t ' + eq_chunks[0] + "&") + if two_dim: + f_output.append('\tJAC(' + str(n[0]) + ', ' + str(n[1]) + ') =\t ' + eq_chunks[0] + "&") + else: + f_output.append('\tF(' + str(n) + ') =\t ' + eq_chunks[0] + "&") for ln in eq_chunks[1:-1]: f_output.append("\t\t&" + ln + "&") f_output.append("\t\t&" + eq_chunks[-1]) else: - f_output.append('\tF(' + str(n) + ') =\t ' + eq_chunks[0]) + if two_dim: + f_output.append('\tJAC(' + str(n[0]) + ', ' + str(n[1]) + ') =\t ' + eq_chunks[0]) + else: + f_output.append('\tF(' + str(n) + ') =\t ' + eq_chunks[0]) f_output.append('') return f_output From 2445742d2b6c969314cef59d493609f89944b8db Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 16 Feb 2024 18:29:56 +0100 Subject: [PATCH 123/143] Symbolic tensor contraction bug fix --- notebooks/Symbolic Output-dynamic_T.ipynb | 316 +++++++++++++++++++++- qgs/functions/symbolic_mul.py | 20 +- qgs/functions/symbolic_tendencies.py | 2 +- qgs/tensors/symbolic_qgtensor.py | 1 - 4 files changed, 319 insertions(+), 20 deletions(-) diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index feef442..97d8f41 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -44,8 +44,7 @@ "source": [ "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT\n", - "from qgs.tensors.symbolic_qgtensor import _shift_dict_keys, _add_to_dict" + "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT" ] }, { @@ -108,6 +107,133 @@ "model_parameters.print_params()" ] }, + { + "cell_type": "markdown", + "id": "9fe98620", + "metadata": {}, + "source": [ + "## Difference between stored and non-stored tensor calculation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f311bba2", + "metadata": {}, + "outputs": [], + "source": [ + "import sparse as sp" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61ce0693", + "metadata": {}, + "outputs": [], + "source": [ + "aip = AtmosphericSymbolicInnerProducts(model_parameters)\n", + "ocn = OceanicSymbolicInnerProducts(model_parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fbca9ad1", + "metadata": {}, + "outputs": [], + "source": [ + "numerical_tensor = QgsTensorDynamicT(model_parameters, aip, ocn)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ac1be7d", + "metadata": {}, + "outputs": [], + "source": [ + "linear_tensor = numerical_tensor._compute_tensor_dicts()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50276cfe", + "metadata": {}, + "outputs": [], + "source": [ + "dyn_tensor_stored = numerical_tensor._compute_stored_full_dict()\n", + "dyn_tensor_non_stored = numerical_tensor._compute_non_stored_full_dict()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15829710", + "metadata": {}, + "outputs": [], + "source": [ + "ndim = model_parameters.ndim\n", + "tensor = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='coo')\n", + "tensor_non_stored = numerical_tensor._add_dict_to_tensor(dyn_tensor_non_stored, tensor)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f5f4c57", + "metadata": {}, + "outputs": [], + "source": [ + "tensor = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='coo')\n", + "tensor_stored = numerical_tensor._add_dict_to_tensor(dyn_tensor_stored, tensor)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c1d6935", + "metadata": {}, + "outputs": [], + "source": [ + "tensor_non_stored" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b1e2cabb", + "metadata": {}, + "outputs": [], + "source": [ + "tensor_stored" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b846872", + "metadata": {}, + "outputs": [], + "source": [ + "for k in tensor_non_stored.coords.T:\n", + " if k[0] == 38:\n", + " print(k)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "830c7e9c", + "metadata": {}, + "outputs": [], + "source": [ + "for k in tensor_stored.coords.T:\n", + " if k[0] == 38:\n", + " print(k)" + ] + }, { "cell_type": "markdown", "id": "d43d341b", @@ -148,29 +274,199 @@ "outputs": [], "source": [ "%%time\n", - "funcs, ten = create_symbolic_equations(model_parameters, continuation_variables=[], return_symbolic_qgtensor=True, language='python')" + "funcs, jac_sym, ten = create_symbolic_equations(model_parameters, continuation_variables=[], return_jacobian=True, return_symbolic_qgtensor=True, language='python')" ] }, { "cell_type": "code", "execution_count": null, - "id": "ecd9ee51", + "id": "a42bae77", "metadata": {}, "outputs": [], "source": [ - "print(funcs)" + "sym_jac = ten.sub_tensor(ten.jac_dic)" ] }, { "cell_type": "code", "execution_count": null, - "id": "a6d2afea", - "metadata": { - "scrolled": true - }, + "id": "1df9daaa", + "metadata": {}, + "outputs": [], + "source": [ + "x = np.ones(model_parameters.ndim)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "764ab808", + "metadata": {}, + "outputs": [], + "source": [ + "np.max(np.abs(Df(0, x)-jac(0, x)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58b4193a", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(36):\n", + " for j in range(36):\n", + " if abs(Df(0, x)[i, j]-jac(0, x)[i, j]) > 1e-7:\n", + " print(i, j, jac(0, x)[i, j])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cb75b0b5", + "metadata": {}, + "outputs": [], + "source": [ + "for i in range(36):\n", + " for j in range(36):\n", + " if abs(jac(0, x)[i, j]) > 0:\n", + " print(i, j, jac(0, x)[i, j])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84755c7d", + "metadata": {}, + "outputs": [], + "source": [ + "c=0\n", + "for k in sym_jac.keys():\n", + " if k[0]==38:\n", + " print(c, k)\n", + " c+=1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "987f26bf", + "metadata": {}, + "outputs": [], + "source": [ + "c=0\n", + "for k in ten_num.jacobian_tensor.coords.T:\n", + " if k[0] == 38:\n", + " print(c, k)\n", + " c+=1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a5e21e64", + "metadata": {}, + "outputs": [], + "source": [ + "c=0\n", + "for k in ten_num.tensor.coords.T:\n", + " if k[0] == 38:\n", + " print(c, k)\n", + " c+=1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "84e8060c", + "metadata": {}, + "outputs": [], + "source": [ + "c=0\n", + "for k in sym_ten.keys():\n", + " if k[0] == 38:\n", + " print(c, k)\n", + " c+=1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2bc281a", + "metadata": {}, + "outputs": [], + "source": [ + "ten_num._compute_stored_full_dict()[38][0].coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "baaa6f0f", + "metadata": {}, + "outputs": [], + "source": [ + "sum(ten_num._compute_stored_full_dict()[38][0].data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb6298e2", + "metadata": {}, + "outputs": [], + "source": [ + "ten_num._compute_non_stored_full_dict()[38].coords" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "09a4bfa6", + "metadata": {}, + "outputs": [], + "source": [ + "ten_num._compute_non_stored_full_dict()[38].data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14c759b8", + "metadata": {}, + "outputs": [], + "source": [ + "sym_ten" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50afda24", + "metadata": {}, + "outputs": [], + "source": [ + "c = 0\n", + "for n, ln in enumerate(ten_num.jacobian_tensor.coords.T):\n", + " delta = ten_num.jacobian_tensor.data[n]- ten_sym[(ln[0], ln[1], ln[2])]\n", + " if abs(delta) > 1e-6:\n", + " print(c, ln, delta)\n", + " \n", + " c+=1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ef8b6d6e", + "metadata": {}, "outputs": [], "source": [ - "ten.print_tensor(ten_num.tensor.todense())" + "c = 0\n", + "for ln in ten_sym.keys():\n", + " if ln[0] == 30:\n", + " print(c, ln)\n", + " c+=1" ] }, { diff --git a/qgs/functions/symbolic_mul.py b/qgs/functions/symbolic_mul.py index ca45d0b..3707ea4 100644 --- a/qgs/functions/symbolic_mul.py +++ b/qgs/functions/symbolic_mul.py @@ -81,8 +81,9 @@ def symbolic_sparse_mult2(dic, vec_a): for key in dic.keys(): coo1, coo2, coo3 = key - val = vec_a[coo3] * dic[key] - res = add_to_dict(res, (coo1, coo2), val) + if coo1 > 0 and coo2 > 0: + val = vec_a[coo3] * dic[key] + res = add_to_dict(res, (coo1, coo2), val) return res @@ -115,8 +116,9 @@ def symbolic_sparse_mult3(dic, vec_a, vec_b): for key in dic.keys(): coo1, coo2, coo3 = key - val = vec_a[coo2] * vec_b[coo3] * dic[key] - res = add_to_dict(res, coo1, val) + if coo1 > 0: + val = vec_a[coo2] * vec_b[coo3] * dic[key] + res = add_to_dict(res, coo1, val) return res @@ -151,8 +153,9 @@ def symbolic_sparse_mult4(dic, vec_a, vec_b, vec_c): for key in dic.keys(): coo1, coo2, coo3, coo4, coo5 = key - val = vec_a[coo3] * vec_b[coo4] * vec_c[coo5] * dic[key] - res = add_to_dict(res, (coo1, coo2), val) + if coo1 > 0 and coo2 > 0: + val = vec_a[coo3] * vec_b[coo4] * vec_c[coo5] * dic[key] + res = add_to_dict(res, (coo1, coo2), val) return res @@ -189,7 +192,8 @@ def symbolic_sparse_mult5(dic, vec_a, vec_b, vec_c, vec_d): for key in dic.keys(): coo1, coo2, coo3, coo4, coo5 = key - val = vec_a[coo2] * vec_b[coo3] * vec_c[coo4] * vec_d[coo5] * dic[key] - res = add_to_dict(res, coo1, val) + if coo1 > 0: + val = vec_a[coo2] * vec_b[coo3] * vec_c[coo4] * vec_d[coo5] * dic[key] + res = add_to_dict(res, coo1, val) return res diff --git a/qgs/functions/symbolic_tendencies.py b/qgs/functions/symbolic_tendencies.py index 585a27c..4d332d4 100644 --- a/qgs/functions/symbolic_tendencies.py +++ b/qgs/functions/symbolic_tendencies.py @@ -181,7 +181,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con ret = list() ret.append(func) if return_jacobian: - ret.append(dict_eq_simplified) + ret.append(func_jac) if return_inner_products: ret.append((aip, oip, gip)) if return_symbolic_eqs: diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 767fd5f..87e6806 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -754,7 +754,6 @@ def jacobian_from_dict(dic): new_pos[1] = orig_order[i+1] new_pos[i+1] = orig_order[1] for key in keys: - dic_jac = add_to_dict(dic_jac, tuple(key[i] for i in new_pos), dic[key]) return dic_jac From 27763d3713355f400eab1510b7fce2a7dc24c36f Mon Sep 17 00:00:00 2001 From: ushham Date: Thu, 22 Feb 2024 16:45:27 +0000 Subject: [PATCH 124/143] Correction of bugs in symbolic dynT - Tests not passing yet --- qgs/params/params.py | 12 ++++++++--- qgs/tensors/symbolic_qgtensor.py | 35 ++++++++++++++++---------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/qgs/params/params.py b/qgs/params/params.py index e79619b..ce71ed2 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -372,6 +372,7 @@ def __init__(self, scale_params, dic=None): self.T0 = None self.sc = None self.hlambda = None + self.dynamic_T = None self.set_params(dic) @@ -394,7 +395,7 @@ def set_insolation(self, value, pos=None, dynamic_T=False): if isinstance(value, (float, int)) and pos is not None and self.C is not None: offset = 1 - if dynamic_T: + if self.dynamic_T or dynamic_T: offset = 0 self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params, description="spectral component "+str(pos+offset)+" of the short-wave radiation of the atmosphere", @@ -418,6 +419,7 @@ def _create_insolation(self, values, dynamic_T=False): offset = 1 if dynamic_T: offset = 0 + self.dynamic_T = True d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the atmosphere" for pos in range(dim)] sy = [Symbol('C_a'+str(pos+offset)) for pos in range(dim)] @@ -543,6 +545,7 @@ def __init__(self, scale_params, dic=None): self.C = None self.T0 = None + self.dynamic_T = None self.set_params(dic) @@ -563,7 +566,7 @@ def set_insolation(self, value, pos=None, dynamic_T=False): if isinstance(value, (float, int)) and pos is not None and self.C is not None: offset = 1 - if dynamic_T: + if self.dynamic_T or dynamic_T: offset = 0 self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params, description="spectral component "+str(pos+offset)+" of the short-wave radiation of the ocean", @@ -587,6 +590,7 @@ def _create_insolation(self, values, dynamic_T=False): offset = 1 if dynamic_T: offset = 0 + self.dynamic_T = True d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the ocean" for pos in range(dim)] sy = [Symbol('C_go'+str(pos+offset)) for pos in range(dim)] @@ -711,6 +715,7 @@ def __init__(self, scale_params, dic=None): self.C = None self.T0 = None + self.dynamic_T = None self.set_params(dic) @@ -733,7 +738,7 @@ def set_insolation(self, value, pos=None, dynamic_T=False): if isinstance(value, (float, int)) and pos is not None and self.C is not None: offset = 1 - if dynamic_T: + if self.dynamic_T or dynamic_T: offset = 0 self.C[pos] = Parameter(value, units='[W][m^-2]', scale_object=self._scale_params, description="spectral component "+str(pos+offset)+" of the short-wave radiation of the ground", @@ -757,6 +762,7 @@ def _create_insolation(self, values, dynamic_T=False): offset = 1 if dynamic_T: offset = 0 + self.dynamic_T = True d = ["spectral component "+str(pos+offset)+" of the short-wave radiation of the ground" for pos in range(dim)] sy = [Symbol('C_go'+str(pos+offset)) for pos in range(dim)] diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index 87e6806..bf43a3d 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -218,8 +218,7 @@ def _compute_tensor_dicts(self): M_psio = dict() for i in range(offset, nvar[3]): for j in range(offset, nvar[3]): - M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.params.G.symbolic_expression \ - * bips.U(i, j) + M_psio[(i - offset, j - offset)] = bips.M(i, j) + self.params.G.symbolic_expression * bips.U(i, j) M_psio = ImmutableSparseMatrix(nvar[2], nvar[2], M_psio) M_psio = M_psio.inverse() @@ -1047,18 +1046,18 @@ def _compute_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.params.T4LSBpa.symbolic_expression * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.params.T4sbpa.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.params.T4LSBpa.symbolic_expression * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.params.T4sbpa.symbolic_expression * val) for m in range(nvar[3]): val = 0 for jj in range(nvar[3]): val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), - self.params.T4LSBpgo.symbolic_expression * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), - self.params.T4sbpgo.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.params.T4LSBpgo.symbolic_expression * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m)), -4 * self.params.T4sbpgo.symbolic_expression * val) if ground_temp: # deltaT_g part @@ -1069,18 +1068,18 @@ def _compute_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.params.T4LSBpa.symbolic_expression * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), self.params.T4sbpa.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.params.T4LSBpa.symbolic_expression * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m)), 4 * self.params.T4sbpa.symbolic_expression * val) for m in range(nvar[2]): val = 0 for jj in range(nvar[2]): val += U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.params.T4LSBpgo.symbolic_expression * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -self.params.T4sbpgo.symbolic_expression * val) else: - sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.params.T4LSBpgo.symbolic_expression * val) + sy_arr_dic = add_to_dict(sy_arr_dic, (self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m)), -4 * self.params.T4sbpgo.symbolic_expression * val) return sy_arr_dic @@ -1181,18 +1180,18 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[3]): val += U_inv[i, jj] * bips.Z(jj, j, k, ell, m) if m == 0: - sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4LSBpa.symbolic_expression * val + sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4sbpa.symbolic_expression * val else: - sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.params.T4LSBpa.symbolic_expression * val + sy_arr_dic[(self._deltaT_o(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.params.T4sbpa.symbolic_expression * val for m in range(nvar[3]): val = 0 for jj in range(nvar[3]): val -= U_inv[i, jj] * bips.V(jj, j, k, ell, m) if m == 0: - sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.params.T4LSBpgo.symbolic_expression * val + sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = self.params.T4sbpgo.symbolic_expression * val else: - sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.params.T4LSBpgo.symbolic_expression * val + sy_arr_dic[(self._deltaT_o(i), self._deltaT_o(j), self._deltaT_o(k), self._deltaT_o(ell), self._deltaT_o(m))] = 4 * self.params.T4sbpgo.symbolic_expression * val # deltaT_g part if ground_temp: @@ -1204,18 +1203,18 @@ def _compute_non_stored_full_dict(self): for jj in range(nvar[2]): val += U_inv[i, jj] * bips._Z[jj, j, k, ell, m] if m == 0: - sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4LSBpa.symbolic_expression * val + sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = self.params.T4sbpa.symbolic_expression * val else: - sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.params.T4LSBpa.symbolic_expression * val + sy_arr_dic[(self._deltaT_g(i), self._theta_a(j), self._theta_a(k), self._theta_a(ell), self._theta_a(m))] = 4 * self.params.T4sbpa.symbolic_expression * val for m in range(nvar[2]): val = 0 for jj in range(nvar[2]): val -= U_inv[i, jj] * bips._V[jj, j, k, ell, m] if m == 0: - sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.params.T4LSBpgo.symbolic_expression * val + sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = self.params.T4sbpgo.symbolic_expression * val else: - sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.params.T4LSBpgo.symbolic_expression * val + sy_arr_dic[(self._deltaT_g(i), self._deltaT_g(j), self._deltaT_g(k), self._deltaT_g(ell), self._deltaT_g(m))] = 4 * self.params.T4sbpgo.symbolic_expression * val return sy_arr_dic From e614e6d8bbf1fbedf16d65f393c1dce0f904abf8 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 23 Feb 2024 11:24:29 +0000 Subject: [PATCH 125/143] Fix of missue of 'connect_to_ocean' in test --- model_test/test_aotensor_sym_dynT.py | 6 ++++-- qgs/tensors/qgtensor.py | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/model_test/test_aotensor_sym_dynT.py b/model_test/test_aotensor_sym_dynT.py index baa16d3..852b2a6 100644 --- a/model_test/test_aotensor_sym_dynT.py +++ b/model_test/test_aotensor_sym_dynT.py @@ -49,7 +49,8 @@ def symbolic_outputs(self, output_func=None): aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) oip = symbolic.OceanicSymbolicInnerProducts(params, return_symbolic=True, make_substitution=True) - aip.connect_to_ocean(oip) + if not aip.connected_to_ocean: + aip.connect_to_ocean(oip) sym_aotensor = SymbolicQgsTensorDynamicT(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) @@ -76,7 +77,8 @@ def numerical_outputs(self, output_func=None): aip = symbolic.AtmosphericSymbolicInnerProducts(params, return_symbolic=False) oip = symbolic.OceanicSymbolicInnerProducts(params, return_symbolic=False) - aip.connect_to_ocean(oip) + if not aip.connected_to_ocean: + aip.connect_to_ocean(oip) num_aotensor = QgsTensorDynamicT(params=params, atmospheric_inner_products=aip, oceanic_inner_products=oip) diff --git a/qgs/tensors/qgtensor.py b/qgs/tensors/qgtensor.py index d4c2d38..5ecfdd7 100644 --- a/qgs/tensors/qgtensor.py +++ b/qgs/tensors/qgtensor.py @@ -977,7 +977,6 @@ def _compute_stored_full_dict(self): sparse_arrays_full_dict[self._theta_a(i)].append(self._shift_tensor_coordinates(- par.T4LSBpgo * val, self._deltaT_o(0))) if ground_temp: - if par.T4LSBpgo is not None: val = sp.tensordot(a_theta[i], aips._v, axes=1) if val.nnz > 0: From 55f08825c9bf5762cd715b51964b38ab54117caa Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 23 Feb 2024 12:41:26 +0000 Subject: [PATCH 126/143] Clean up of function outputs - Tests passing --- notebooks/Symbolic Output-dynamic_T.ipynb | 709 +--------------------- qgs/functions/symbolic_tendencies.py | 106 ++-- 2 files changed, 75 insertions(+), 740 deletions(-) diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb index 97d8f41..a27cc1a 100644 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ b/notebooks/Symbolic Output-dynamic_T.ipynb @@ -21,7 +21,8 @@ "import numpy as np\n", "import sympy as sy\n", "import sparse as sp\n", - "import math" + "import math\n", + "from numba import njit" ] }, { @@ -32,7 +33,8 @@ "outputs": [], "source": [ "from qgs.params.params import QgParams\n", - "from qgs.functions.tendencies import create_tendencies" + "from qgs.functions.tendencies import create_tendencies\n", + "from qgs.functions.symbolic_tendencies import create_symbolic_equations" ] }, { @@ -107,722 +109,88 @@ "model_parameters.print_params()" ] }, - { - "cell_type": "markdown", - "id": "9fe98620", - "metadata": {}, - "source": [ - "## Difference between stored and non-stored tensor calculation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f311bba2", - "metadata": {}, - "outputs": [], - "source": [ - "import sparse as sp" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "61ce0693", - "metadata": {}, - "outputs": [], - "source": [ - "aip = AtmosphericSymbolicInnerProducts(model_parameters)\n", - "ocn = OceanicSymbolicInnerProducts(model_parameters)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fbca9ad1", - "metadata": {}, - "outputs": [], - "source": [ - "numerical_tensor = QgsTensorDynamicT(model_parameters, aip, ocn)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9ac1be7d", - "metadata": {}, - "outputs": [], - "source": [ - "linear_tensor = numerical_tensor._compute_tensor_dicts()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "50276cfe", - "metadata": {}, - "outputs": [], - "source": [ - "dyn_tensor_stored = numerical_tensor._compute_stored_full_dict()\n", - "dyn_tensor_non_stored = numerical_tensor._compute_non_stored_full_dict()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "15829710", - "metadata": {}, - "outputs": [], - "source": [ - "ndim = model_parameters.ndim\n", - "tensor = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='coo')\n", - "tensor_non_stored = numerical_tensor._add_dict_to_tensor(dyn_tensor_non_stored, tensor)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6f5f4c57", - "metadata": {}, - "outputs": [], - "source": [ - "tensor = sp.zeros((ndim + 1, ndim + 1, ndim + 1, ndim + 1, ndim + 1), dtype=np.float64, format='coo')\n", - "tensor_stored = numerical_tensor._add_dict_to_tensor(dyn_tensor_stored, tensor)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c1d6935", - "metadata": {}, - "outputs": [], - "source": [ - "tensor_non_stored" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b1e2cabb", - "metadata": {}, - "outputs": [], - "source": [ - "tensor_stored" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7b846872", - "metadata": {}, - "outputs": [], - "source": [ - "for k in tensor_non_stored.coords.T:\n", - " if k[0] == 38:\n", - " print(k)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "830c7e9c", - "metadata": {}, - "outputs": [], - "source": [ - "for k in tensor_stored.coords.T:\n", - " if k[0] == 38:\n", - " print(k)" - ] - }, - { - "cell_type": "markdown", - "id": "d43d341b", - "metadata": {}, - "source": [ - "## Testing the symbolic_qgtensor" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e38656d8", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.functions.symbolic_tendencies import create_symbolic_equations, translate_equations, equation_as_function" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a64d4acf", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c73f1cd9", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "funcs, jac_sym, ten = create_symbolic_equations(model_parameters, continuation_variables=[], return_jacobian=True, return_symbolic_qgtensor=True, language='python')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a42bae77", - "metadata": {}, - "outputs": [], - "source": [ - "sym_jac = ten.sub_tensor(ten.jac_dic)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1df9daaa", - "metadata": {}, - "outputs": [], - "source": [ - "x = np.ones(model_parameters.ndim)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "764ab808", - "metadata": {}, - "outputs": [], - "source": [ - "np.max(np.abs(Df(0, x)-jac(0, x)))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "58b4193a", - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(36):\n", - " for j in range(36):\n", - " if abs(Df(0, x)[i, j]-jac(0, x)[i, j]) > 1e-7:\n", - " print(i, j, jac(0, x)[i, j])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cb75b0b5", - "metadata": {}, - "outputs": [], - "source": [ - "for i in range(36):\n", - " for j in range(36):\n", - " if abs(jac(0, x)[i, j]) > 0:\n", - " print(i, j, jac(0, x)[i, j])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "84755c7d", - "metadata": {}, - "outputs": [], - "source": [ - "c=0\n", - "for k in sym_jac.keys():\n", - " if k[0]==38:\n", - " print(c, k)\n", - " c+=1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "987f26bf", - "metadata": {}, - "outputs": [], - "source": [ - "c=0\n", - "for k in ten_num.jacobian_tensor.coords.T:\n", - " if k[0] == 38:\n", - " print(c, k)\n", - " c+=1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a5e21e64", - "metadata": {}, - "outputs": [], - "source": [ - "c=0\n", - "for k in ten_num.tensor.coords.T:\n", - " if k[0] == 38:\n", - " print(c, k)\n", - " c+=1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "84e8060c", - "metadata": {}, - "outputs": [], - "source": [ - "c=0\n", - "for k in sym_ten.keys():\n", - " if k[0] == 38:\n", - " print(c, k)\n", - " c+=1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b2bc281a", - "metadata": {}, - "outputs": [], - "source": [ - "ten_num._compute_stored_full_dict()[38][0].coords" - ] - }, { "cell_type": "code", "execution_count": null, - "id": "baaa6f0f", + "id": "f3b012e9", "metadata": {}, "outputs": [], "source": [ - "sum(ten_num._compute_stored_full_dict()[38][0].data)" + "f_num, Df = create_tendencies(model_parameters)" ] }, { "cell_type": "code", "execution_count": null, - "id": "eb6298e2", + "id": "8552c7a9", "metadata": {}, "outputs": [], "source": [ - "ten_num._compute_non_stored_full_dict()[38].coords" + "f_sym, jac_sym = create_symbolic_equations(model_parameters, language='python', continuation_variables=[model_parameters.atemperature_params.eps, model_parameters.atmospheric_params.kd], return_jacobian=True)" ] }, { "cell_type": "code", "execution_count": null, - "id": "09a4bfa6", + "id": "0a5bff28", "metadata": {}, "outputs": [], "source": [ - "ten_num._compute_non_stored_full_dict()[38].data" + "print(f_sym)" ] }, { "cell_type": "code", "execution_count": null, - "id": "14c759b8", + "id": "ded58816", "metadata": {}, "outputs": [], "source": [ - "sym_ten" + "print(jac_sym)" ] }, { "cell_type": "code", "execution_count": null, - "id": "50afda24", + "id": "469ff2a6", "metadata": {}, "outputs": [], "source": [ - "c = 0\n", - "for n, ln in enumerate(ten_num.jacobian_tensor.coords.T):\n", - " delta = ten_num.jacobian_tensor.data[n]- ten_sym[(ln[0], ln[1], ln[2])]\n", - " if abs(delta) > 1e-6:\n", - " print(c, ln, delta)\n", - " \n", - " c+=1" + "exec(f_sym)" ] }, { "cell_type": "code", "execution_count": null, - "id": "ef8b6d6e", - "metadata": {}, - "outputs": [], - "source": [ - "c = 0\n", - "for ln in ten_sym.keys():\n", - " if ln[0] == 30:\n", - " print(c, ln)\n", - " c+=1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "27664807", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.functions.symbolic_mul import symbolic_sparse_mult5\n", - "from qgs.functions.symbolic_tendencies import equation_as_function" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f3a76194", - "metadata": {}, - "outputs": [], - "source": [ - "xx = list()\n", - "xx.append(1)\n", - "for i in range(1, 39):\n", - " xx.append(sy.Symbol('U[' + str(i-1) + ']'))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2f044536", - "metadata": {}, - "outputs": [], - "source": [ - "fun_out = symbolic_sparse_mult5(ten.tensor_dic, xx, xx, xx, xx)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "587e2a50", - "metadata": {}, - "outputs": [], - "source": [ - "test_fun = '\\n'.join(equation_as_function(fun_out, model_parameters))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "58bfe429", - "metadata": {}, - "outputs": [], - "source": [ - "print(test_fun)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c6f526dc", - "metadata": {}, - "outputs": [], - "source": [ - "def arr_to_dict(arr):\n", - " x, y, z, w, s = arr.shape\n", - " output = dict()\n", - " for i in range(x):\n", - " for j in range(y):\n", - " for k in range(z):\n", - " for l in range(w):\n", - " for m in range(s):\n", - " if arr[i, j, k, l, m] != 0:\n", - " output[(i, j, k, l, m)] = arr[i, j, k, l, m]\n", - " return output" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "25e91755", - "metadata": {}, - "outputs": [], - "source": [ - "ten_num_dic = arr_to_dict(ten_num.tensor.todense())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2d899609", - "metadata": {}, - "outputs": [], - "source": [ - "ten_dic = ten.sub_tensor()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9d1f7603", - "metadata": {}, - "outputs": [], - "source": [ - "ten_dic" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "093b86d5", - "metadata": {}, - "outputs": [], - "source": [ - "ten_num_dic" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1a500d10", - "metadata": {}, - "outputs": [], - "source": [ - "for k in ten_num_dic.keys():\n", - " if ten_dic[k] - ten_num_dic[k] != 0:\n", - " print(k, (ten_dic[k] - ten_num_dic[k]) / ten_num_dic[k])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9001d905", - "metadata": {}, - "outputs": [], - "source": [ - "test_eq = symbolic_sparse_mult5(ten_num_dic, xx, xx, xx, xx)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "64fc47ac", - "metadata": {}, - "outputs": [], - "source": [ - "import sparse as sp" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f97405e2", - "metadata": {}, - "outputs": [], - "source": [ - "subbed_tensor = ten.sub_tensor()\n", - "dims = (model_parameters.ndim + 1, model_parameters.ndim + 1, model_parameters.ndim + 1, model_parameters.ndim + 1, model_parameters.ndim + 1)\n", - "coords = np.array([list(k) for k in subbed_tensor.keys()]).T\n", - "data = np.array(list(subbed_tensor.values()), dtype=float)\n", - "subbed_tensor_sp = sp.COO(coords, data, shape=dims)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6d842e3d", - "metadata": {}, - "outputs": [], - "source": [ - "num_t = ten_num.tensor.todense()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4c62f70f", - "metadata": {}, - "outputs": [], - "source": [ - "err = subbed_tensor_sp.todense() - num_t" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "14fc36c9", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "for i in range(model_parameters.ndim + 1):\n", - " for j in range(model_parameters.ndim + 1):\n", - " for k in range(model_parameters.ndim + 1):\n", - " for ell in range(model_parameters.ndim + 1):\n", - " for m in range(model_parameters.ndim + 1):\n", - " if abs(err[i, j, k, ell, m]) > 1e-15:\n", - " if num_t[i, j, k, ell, m] != 0:\n", - " print(i, j, k, ell, m, err[i, j, k, ell, m])\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5970236e", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "ten.print_tensor(num_t)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e14f2e2", + "id": "cd4adc73", "metadata": {}, "outputs": [], "source": [ - "from numba import njit" + "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", + "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": null, - "id": "9266e59c", + "id": "59922db7", "metadata": {}, "outputs": [], "source": [ - "@njit\n", - "def f(t, U):\n", - "\t#Tendency function of the qgs model\n", - "\tF = np.empty_like(U)\n", - "\tF[0] = -0.0145*U[0] + 0.0145*U[11] + 0.0505574149661188*U[22] + 0.0734121368001177*U[24]\n", - "\tF[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[11]*U[13] + 0.0145*U[12] - 1.9945469157942*U[14]*U[16] - 2.59615384615385*U[15]*U[18] + 2.59615384615385*U[16]*U[17] - 0.0145*U[1] - 0.00295864958311857*U[21] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", - "\tF[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[11]*U[12] + 0.0145*U[13] + 1.9945469157942*U[14]*U[15] + 2.59615384615385*U[15]*U[17] + 2.59615384615385*U[16]*U[18] - 0.115315741885204*U[1] + 0.0145*U[25] - 0.0145*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", - "\tF[3] = 2.16075915877706*U[12]*U[16] - 2.16075915877706*U[13]*U[15] + 0.0145*U[14] + 4.32151831755411*U[17]*U[20] - 4.32151831755411*U[18]*U[19] + 2.16075915877706*U[1]*U[5] - 0.00216427290094687*U[21] + 0.0238416302768307*U[23] - 2.16075915877706*U[2]*U[4] - 0.0145*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", - "\tF[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[11]*U[16] + 2.43*U[12]*U[18] + 0.345721465404329*U[13]*U[14] - 2.43*U[13]*U[17] + 0.0145*U[15] + 2.43*U[1]*U[7] - 0.00449241352700723*U[22] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.0145*U[4] + 0.059964185780306*U[5]\n", - "\tF[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[11]*U[15] - 0.345721465404329*U[12]*U[14] - 2.43*U[12]*U[17] - 2.43*U[13]*U[18] + 0.0145*U[16] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.0145*U[26] - 2.43*U[2]*U[7] - 0.059964185780306*U[4] - 0.0145*U[5]\n", - "\tF[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[11]*U[18] + 0.675*U[12]*U[16] + 0.675*U[13]*U[15] - 5.18582198106493*U[14]*U[20] + 0.0145*U[17] + 0.675*U[1]*U[5] - 0.000192312222902707*U[21] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.0145*U[6] + 0.0749552322253824*U[7]\n", - "\tF[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[11]*U[17] - 0.675*U[12]*U[15] + 0.675*U[13]*U[16] + 5.18582198106493*U[14]*U[19] + 0.0145*U[18] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.0145*U[7]\n", - "\tF[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[11]*U[20] - 2.65939588772561*U[14]*U[18] + 0.0145*U[19] - 0.000431962839135312*U[22] - 2.65939588772561*U[3]*U[7] - 0.0145*U[8] + 0.0576578709426019*U[9]\n", - "\tF[9] = 2.6593958877256*U[0]*U[8] + 2.6593958877256*U[11]*U[19] + 2.6593958877256*U[14]*U[17] + 0.0145*U[20] + 2.6593958877256*U[3]*U[6] - 0.0576578709426019*U[8] - 0.0145*U[9]\n", - "\tF[10] = -0.000510753549879668*U[10]**4 - 0.014593023255814*U[10] - 0.00183644645351737*U[21] - 0.00374635076517544*U[23] + 1.59610484337396e-5*U[29]**4 + 5.17501542233338e-5*U[29]**3*U[30] + 1.72500514077779e-5*U[29]**3*U[32] + 0.00729651162790698*U[29] + 0.00591432955679693*U[30] + 0.00197144318559898*U[32] + 0.000531003677198668\n", - "\tF[11] = 0.00131818181818182*U[0] - 0.00185728563592607*U[10]**3*U[11] - 0.0198572938689218*U[11] - 1.63693875664928*U[12]*U[2] + 1.63693875664928*U[13]*U[1] - 1.30955100531943*U[15]*U[5] + 1.30955100531943*U[16]*U[4] - 3.27387751329857*U[17]*U[7] + 3.27387751329857*U[18]*U[6] - 2.61910201063885*U[19]*U[9] + 2.61910201063885*U[20]*U[8] - 0.00459612863328353*U[22] - 0.00667383061819252*U[24] + 4.43550120954776e-5*U[29]**3*U[31] + 1.7742004838191e-5*U[29]**3*U[33] + 0.00506916671004016*U[31] + 0.00202766668401606*U[33] + 0.000482730615635153\n", - "\tF[12] = -1.6647358298754*U[0]*U[13] - 0.0015418975090707*U[10]**3*U[12] + 1.05320021890077*U[11]*U[2] - 0.0287966213251426*U[12] + 0.0282849932925971*U[13] + 1.68512035024122*U[14]*U[5] + 1.06132075471698*U[15]*U[7] - 2.66357732780065*U[16]*U[3] - 1.06132075471698*U[16]*U[6] + 2.33490566037736*U[17]*U[5] - 2.33490566037736*U[18]*U[4] + 0.0035566037735849*U[1] + 0.000725706501519649*U[21] - 2.04500508591402e-5*U[29]**3*U[30] - 0.00233715902975374*U[30]\n", - "\tF[13] = 1.6647358298754*U[0]*U[12] - 0.0015418975090707*U[10]**3*U[13] - 1.05320021890077*U[11]*U[1] - 0.0282849932925971*U[12] - 0.0287966213251426*U[13] - 1.68512035024122*U[14]*U[4] + 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] - 1.06132075471698*U[16]*U[7] + 2.33490566037736*U[17]*U[4] + 2.33490566037736*U[18]*U[5] - 0.0035566037735849*U[25] + 4.81842971584593e-5*U[29]**3*U[34] + 0.0035566037735849*U[2] + 0.00550680122860904*U[34]\n", - "\tF[14] = -0.00145929585679905*U[10]**3*U[14] - 1.44050610585137*U[12]*U[5] + 1.44050610585137*U[13]*U[4] - 0.0311378737541528*U[14] - 2.67522562515254*U[15]*U[2] + 2.67522562515254*U[16]*U[1] - 2.88101221170274*U[17]*U[9] + 2.88101221170274*U[18]*U[8] - 5.35045125030509*U[19]*U[7] + 5.35045125030509*U[20]*U[6] + 0.000618363685984819*U[21] - 0.00681189436480877*U[23] - 1.74251833232233e-5*U[29]**3*U[30] + 3.1365329981802e-5*U[29]**3*U[32] - 0.00199145835037292*U[30] + 0.00358462503067126*U[32] + 0.00414285714285714*U[3]\n", - "\tF[15] = -1.35185957626052*U[0]*U[16] - 0.00125723950739611*U[10]**3*U[15] + 0.421071015556554*U[11]*U[5] - 0.449999999999999*U[12]*U[7] - 1.63996079743079*U[13]*U[3] + 0.45*U[13]*U[6] + 1.90590038620335*U[14]*U[2] - 0.0368649373881932*U[15] + 0.0230631483770408*U[16] - 2.31923076923077*U[17]*U[2] + 2.31923076923077*U[18]*U[1] + 0.00172785135654124*U[22] - 1.66746568543758e-5*U[29]**3*U[31] - 0.00190568351656843*U[31] + 0.00557692307692308*U[4]\n", - "\tF[16] = 1.35185957626052*U[0]*U[15] - 0.00125723950739611*U[10]**3*U[16] - 0.421071015556554*U[11]*U[4] + 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 0.45*U[13]*U[7] - 1.90590038620335*U[14]*U[1] - 0.0230631483770408*U[15] - 0.0368649373881932*U[16] - 2.31923076923077*U[17]*U[1] - 2.31923076923077*U[18]*U[2] - 0.00557692307692308*U[26] + 3.92887346061283e-5*U[29]**3*U[35] + 0.00449016100178891*U[35] + 0.00557692307692308*U[5]\n", - "\tF[17] = -3.421202001397*U[0]*U[18] - 0.00102150709975934*U[10]**3*U[17] + 0.180063263231421*U[11]*U[7] - 0.7875*U[12]*U[5] - 0.7875*U[13]*U[4] + 0.288101221170274*U[14]*U[9] + 1.4625*U[15]*U[2] + 1.4625*U[16]*U[1] - 0.043546511627907*U[17] + 0.0374776161126912*U[18] - 5.47392320223521*U[20]*U[3] + 9.61561114513536e-5*U[21] - 2.70963173883608e-6*U[29]**3*U[30] - 0.00030967357144237*U[30] + 0.00725*U[6]\n", - "\tF[18] = 3.421202001397*U[0]*U[17] - 0.00102150709975934*U[10]**3*U[18] - 0.180063263231421*U[11]*U[6] + 0.7875*U[12]*U[4] - 0.7875*U[13]*U[5] - 0.288101221170275*U[14]*U[8] - 1.4625*U[15]*U[1] + 1.4625*U[16]*U[2] - 0.0374776161126912*U[17] - 0.043546511627907*U[18] + 5.47392320223521*U[19]*U[3] + 0.00725*U[7]\n", - "\tF[19] = -2.75575081119392*U[0]*U[20] - 0.000888267043268989*U[10]**3*U[19] - 0.25052280101763*U[11]*U[9] + 1.00209120407052*U[14]*U[7] - 4.00836481628207*U[18]*U[3] - 0.0473230535894843*U[19] + 0.0325892314023402*U[20] + 0.00024415290907648*U[22] - 2.35620151203137e-6*U[29]**3*U[31] - 0.000269281366471626*U[31] + 0.00819565217391304*U[8]\n", - "\tF[20] = 2.75575081119392*U[0]*U[19] - 0.000888267043268989*U[10]**3*U[20] + 0.250522801017629*U[11]*U[8] - 1.00209120407052*U[14]*U[6] + 4.00836481628207*U[17]*U[3] - 0.0325892314023402*U[19] - 0.0473230535894843*U[20] + 0.00819565217391304*U[9]\n", - "\tF[21] = 2.30554431681946e-7*U[12] + 2.5547312812995e-7*U[14] + 1.41879650265813e-7*U[17] - 2.30554431681946e-7*U[1] - 4.98594961275655e-7*U[21] - 0.000231547500016414*U[22]*U[25] - 0.000393263214313593*U[22]*U[27] - 0.000779175714340951*U[23]*U[26] - 0.00102174928578672*U[23]*U[28] - 0.00156202678582502*U[24]*U[27] - 2.49430228227487e-5*U[25] - 2.5547312812995e-7*U[3] - 1.41879650265813e-7*U[6]\n", - "\tF[22] = 1.27676499203106e-7*U[0] - 1.27676499203106e-7*U[11] + 4.43165421807242e-7*U[15] + 1.84356815471813e-7*U[19] - 0.000297563940911795*U[21]*U[25] + 0.00056941247952257*U[21]*U[27] - 1.45521268835226e-6*U[22] - 0.00185518259457354*U[23]*U[25] - 0.00363689261114416*U[24]*U[26] - 2.49312940023328e-5*U[26] - 4.43165421807242e-7*U[4] - 1.84356815471813e-7*U[8]\n", - "\tF[23] = -4.59275459509596e-7*U[14] - 0.00110122563125871*U[21]*U[26] + 0.0019601816236405*U[21]*U[28] + 0.000385428970940549*U[22]*U[25] - 3.04757790976247e-6*U[23] - 0.00547309138735578*U[24]*U[25] - 2.49117704610167e-5*U[27] + 4.59275459509596e-7*U[3]\n", - "\tF[24] = 5.09747214298138e-8*U[0] - 5.09747214298138e-8*U[11] - 0.0028417174170155*U[21]*U[27] - 0.000594010608460015*U[22]*U[26] + 0.00259237963074834*U[23]*U[25] - 5.2727039845599e-6*U[24] - 2.48844888171634e-5*U[28]\n", - "\tF[25] = -5.43087366736076e-7*U[13] + 0.000529111412123567*U[21]*U[22] + 2.49364240037345e-5*U[21] + 0.00146975392256546*U[22]*U[23] + 0.00288071768822831*U[23]*U[24] - 1.03680315467796e-6*U[25] + 5.43087366736076e-7*U[2]\n", - "\tF[26] = -1.04390781015316e-6*U[16] + 0.00188040062849988*U[21]*U[23] + 0.00423090141412473*U[22]*U[24] + 2.49247013868922e-5*U[22] - 1.99291491029239e-6*U[26] + 1.04390781015316e-6*U[5]\n", - "\tF[27] = -0.000176149545706673*U[21]*U[22] + 0.00440373864266684*U[21]*U[24] + 2.49051881654425e-5*U[23] - 3.58443843007766e-6*U[27]\n", - "\tF[28] = -0.000938435679208949*U[21]*U[23] + 2.4877920928742e-5*U[24] - 5.80838943890229e-6*U[28]\n", - "\tF[29] = 9.12059910499408e-6*U[10]**4 + 4.64878611115251e-5*U[10]**3*U[12] + 1.67415119447806e-5*U[10]**3*U[14] + 9.29757222230503e-6*U[10]**3*U[17] + 0.000521179401993355*U[10] + 0.000664115245477357*U[12] + 0.000239165516524778*U[14] + 0.000132823049095472*U[17] + 1.50119582264922*U[21]*U[35] - 0.750597911324608*U[21]*U[37] - 1.50119582264922*U[22]*U[34] + 1.12589686698691*U[22]*U[36] - 4.50358746794765*U[23]*U[35] + 2.25179373397383*U[23]*U[37] - 2.62709268963613*U[24]*U[34] - 5.62948433493457*U[24]*U[36] + 1.50119582264922*U[25]*U[31] + 2.62709268963613*U[25]*U[33] - 1.50119582264922*U[26]*U[30] + 4.50358746794765*U[26]*U[32] - 1.12589686698691*U[27]*U[31] + 5.62948433493457*U[27]*U[33] + 0.750597911324608*U[28]*U[30] - 2.25179373397383*U[28]*U[32] - 8.14339205803043e-7*U[29]**4 - 1.44655701782e-21*U[29]**3*U[30] - 0.000260589700996678*U[29] + 5.70773716822424e-5\n", - "\tF[30] = -5.06294956237001e-22*U[10]**4 - 5.31652508387687e-5*U[10]**3*U[12] - 2.75103051082387e-5*U[10]**3*U[14] - 1.06330501677538e-5*U[10]**3*U[17] - 2.89312686026697e-20*U[10] - 0.000759506950146621*U[12] - 0.000393006100803065*U[14] - 0.000151901390029324*U[17] - 1.21682350103801*U[21]*U[35] + 0.608411750519007*U[21]*U[37] + 2.34182350103801*U[22]*U[34] - 1.28761762577851*U[22]*U[36] + 5.15047050311404*U[23]*U[35] - 2.57523525155702*U[23]*U[37] + 2.12944112681652*U[24]*U[34] + 6.43808812889255*U[24]*U[36] - 2.34182350103801*U[25]*U[31] - 2.12944112681652*U[25]*U[33] + 1.21682350103801*U[26]*U[30] - 5.15047050311404*U[26]*U[32] + 1.28761762577851*U[27]*U[31] - 6.43808812889255*U[27]*U[33] - 0.608411750519007*U[28]*U[30] + 2.57523525155702*U[28]*U[32] + 4.52049068068751e-23*U[29]**4 - 3.25735682321217e-6*U[29]**3*U[30] - 3.61639254455001e-22*U[29]**3*U[32] - 0.000260589700996678*U[30] - 3.16843061133567e-21\n", - "\tF[31] = 2.78802933171573e-5*U[10]**3*U[11] - 1.54836099362061e-5*U[10]**3*U[15] - 3.09672198724123e-6*U[10]**3*U[19] + 0.000398291670074584*U[11] - 0.000221195408173122*U[15] - 4.42390816346244e-5*U[19] - 1.125*U[21]*U[34] + 0.375*U[21]*U[36] + 1.875*U[23]*U[34] + 2.25*U[24]*U[35] + 1.125*U[25]*U[30] - 1.875*U[25]*U[32] - 2.25*U[26]*U[33] - 0.375*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[31] - 0.000260589700996678*U[31] + 4.36192251724445e-5\n", - "\tF[32] = -5.06294956237001e-22*U[10]**4 - 1.25605469675209e-5*U[10]**3*U[12] + 2.05688778355549e-5*U[10]**3*U[14] - 2.51210939350418e-6*U[10]**3*U[17] - 2.89312686026697e-20*U[10] - 0.000179437180657833*U[12] + 0.000293842414478535*U[14] - 3.58874361315667e-5*U[17] - 1.90560783367934*U[21]*U[35] + 0.952803916839669*U[21]*U[37] - 1.46939216632066*U[22]*U[34] - 0.304205875259504*U[22]*U[36] + 1.21682350103801*U[23]*U[35] - 0.608411750519007*U[23]*U[37] + 3.33481370893884*U[24]*U[34] + 1.52102937629752*U[24]*U[36] + 1.46939216632066*U[25]*U[31] - 3.33481370893884*U[25]*U[33] + 1.90560783367934*U[26]*U[30] - 1.21682350103801*U[26]*U[32] + 0.304205875259504*U[27]*U[31] - 1.52102937629752*U[27]*U[33] - 0.952803916839669*U[28]*U[30] + 0.608411750519007*U[28]*U[32] + 4.52049068068751e-23*U[29]**4 + 3.61639254455001e-22*U[29]**3*U[30] - 3.25735682321217e-6*U[29]**3*U[32] - 0.000260589700996678*U[32] - 3.16843061133567e-21\n", - "\tF[33] = 1.11521173268629e-5*U[10]**3*U[11] + 0.000159316668029834*U[11] - 1.875*U[21]*U[36] - 2.25*U[22]*U[35] - 2.625*U[23]*U[34] + 2.625*U[25]*U[32] + 2.25*U[26]*U[31] + 1.875*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[33] - 0.000260589700996678*U[33] + 1.74476900689778e-5\n", - "\tF[34] = 3.64823964199763e-5*U[10]**3*U[13] + 0.000521179401993355*U[13] + 1.125*U[21]*U[31] - 1.125*U[22]*U[30] + 1.875*U[22]*U[32] - 1.875*U[23]*U[31] + 2.625*U[23]*U[33] - 2.625*U[24]*U[32] - 3.25735682321217e-6*U[29]**3*U[34] - 0.000260589700996678*U[34]\n", - "\tF[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", - "\tF[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", - "\tF[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", - "\treturn F" + "eps = model_parameters.atemperature_params.eps\n", + "kd = model_parameters.atmospheric_params.kd" ] }, { "cell_type": "code", "execution_count": null, - "id": "5b8e33b0", + "id": "edc906e8", "metadata": {}, "outputs": [], "source": [ "@njit\n", - "def f_test(t, U):\n", - "\t#Tendency function of the qgs model\n", - "\tF = np.empty_like(U)\n", - "\tF[0] = -0.0145*U[0] + 0.0145*U[11] + 0.0505574149661188*U[22] + 0.0734121368001177*U[24]\n", - "\tF[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[11]*U[13] + 0.0145*U[12] - 1.9945469157942*U[14]*U[16] - 2.59615384615385*U[15]*U[18] + 2.59615384615385*U[16]*U[17] - 0.0145*U[1] - 0.00295864958311857*U[21] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", - "\tF[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[11]*U[12] + 0.0145*U[13] + 1.9945469157942*U[14]*U[15] + 2.59615384615385*U[15]*U[17] + 2.59615384615385*U[16]*U[18] - 0.115315741885204*U[1] + 0.0145*U[25] - 0.0145*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", - "\tF[3] = 2.16075915877706*U[12]*U[16] - 2.16075915877706*U[13]*U[15] + 0.0145*U[14] + 4.32151831755411*U[17]*U[20] - 4.32151831755411*U[18]*U[19] + 2.16075915877706*U[1]*U[5] - 0.00216427290094687*U[21] + 0.0238416302768307*U[23] - 2.16075915877706*U[2]*U[4] - 0.0145*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", - "\tF[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[11]*U[16] + 2.43*U[12]*U[18] + 0.345721465404329*U[13]*U[14] - 2.43*U[13]*U[17] + 0.0145*U[15] + 2.43*U[1]*U[7] - 0.00449241352700723*U[22] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.0145*U[4] + 0.059964185780306*U[5]\n", - "\tF[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[11]*U[15] - 0.345721465404329*U[12]*U[14] - 2.43*U[12]*U[17] - 2.43*U[13]*U[18] + 0.0145*U[16] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.0145*U[26] - 2.43*U[2]*U[7] - 0.059964185780306*U[4] - 0.0145*U[5]\n", - "\tF[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[11]*U[18] + 0.675*U[12]*U[16] + 0.675*U[13]*U[15] - 5.18582198106493*U[14]*U[20] + 0.0145*U[17] + 0.675*U[1]*U[5] - 0.000192312222902707*U[21] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.0145*U[6] + 0.0749552322253824*U[7]\n", - "\tF[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[11]*U[17] - 0.675*U[12]*U[15] + 0.675*U[13]*U[16] + 5.18582198106493*U[14]*U[19] + 0.0145*U[18] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.0145*U[7]\n", - "\tF[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[11]*U[20] - 2.6593958877256*U[14]*U[18] + 0.0145*U[19] - 0.000431962839135312*U[22] - 2.6593958877256*U[3]*U[7] - 0.0145*U[8] + 0.0576578709426019*U[9]\n", - "\tF[9] = 2.6593958877256*U[0]*U[8] + 2.6593958877256*U[11]*U[19] + 2.6593958877256*U[14]*U[17] + 0.0145*U[20] + 2.6593958877256*U[3]*U[6] - 0.0576578709426019*U[8] - 0.0145*U[9]\n", - "\tF[10] = -0.000510753549879668*U[10]**4 - 0.014593023255814*U[10] - 0.00183644645351737*U[21] - 0.00374635076517544*U[23] + 1.59610484337396e-5*U[29]**4 + 5.17501542233338e-5*U[29]**3*U[30] + 1.72500514077779e-5*U[29]**3*U[32] + 0.00729651162790698*U[29] + 0.00591432955679693*U[30] + 0.00197144318559898*U[32] + 0.000531003677198668\n", - "\tF[11] = 0.00131818181818182*U[0] - 0.00185728563592607*U[10]**3*U[11] - 0.0198572938689218*U[11] - 1.63693875664928*U[12]*U[2] + 1.63693875664928*U[13]*U[1] - 1.30955100531943*U[15]*U[5] + 1.30955100531943*U[16]*U[4] - 3.27387751329857*U[17]*U[7] + 3.27387751329857*U[18]*U[6] - 2.61910201063885*U[19]*U[9] + 2.61910201063885*U[20]*U[8] - 0.00459612863328353*U[22] - 0.00667383061819252*U[24] + 4.43550120954775e-5*U[29]**3*U[31] + 1.7742004838191e-5*U[29]**3*U[33] + 0.00506916671004016*U[31] + 0.00202766668401607*U[33] + 0.000482730615635153\n", - "\tF[12] = -1.6647358298754*U[0]*U[13] - 0.0015418975090707*U[10]**3*U[12] + 1.05320021890077*U[11]*U[2] - 0.0287966213251426*U[12] + 0.0282849932925971*U[13] + 1.68512035024122*U[14]*U[5] + 1.06132075471698*U[15]*U[7] - 2.66357732780065*U[16]*U[3] - 1.06132075471698*U[16]*U[6] + 2.33490566037736*U[17]*U[5] - 2.33490566037736*U[18]*U[4] + 0.00355660377358491*U[1] + 0.000725706501519649*U[21] - 2.04500508591402e-5*U[29]**3*U[30] - 0.00233715902975374*U[30]\n", - "\tF[13] = 1.6647358298754*U[0]*U[12] - 0.0015418975090707*U[10]**3*U[13] - 1.05320021890077*U[11]*U[1] - 0.0282849932925971*U[12] - 0.0287966213251426*U[13] - 1.68512035024122*U[14]*U[4] + 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] - 1.06132075471698*U[16]*U[7] + 2.33490566037736*U[17]*U[4] + 2.33490566037736*U[18]*U[5] - 0.00355660377358491*U[25] + 4.81842971584593e-5*U[29]**3*U[34] + 0.00355660377358491*U[2] + 0.00550680122860904*U[34]\n", - "\tF[14] = -0.00145929585679905*U[10]**3*U[14] - 1.44050610585137*U[12]*U[5] + 1.44050610585137*U[13]*U[4] - 0.0311378737541528*U[14] - 2.67522562515255*U[15]*U[2] + 2.67522562515255*U[16]*U[1] - 2.88101221170274*U[17]*U[9] + 2.88101221170274*U[18]*U[8] - 5.35045125030509*U[19]*U[7] + 5.35045125030509*U[20]*U[6] + 0.000618363685984819*U[21] - 0.00681189436480877*U[23] - 1.74251833232233e-5*U[29]**3*U[30] + 3.1365329981802e-5*U[29]**3*U[32] - 0.00199145835037292*U[30] + 0.00358462503067126*U[32] + 0.00414285714285714*U[3]\n", - "\tF[15] = -1.35185957626052*U[0]*U[16] - 0.00125723950739611*U[10]**3*U[15] + 0.421071015556554*U[11]*U[5] - 0.45*U[12]*U[7] - 1.63996079743079*U[13]*U[3] + 0.45*U[13]*U[6] + 1.90590038620335*U[14]*U[2] - 0.0368649373881932*U[15] + 0.0230631483770408*U[16] - 2.31923076923077*U[17]*U[2] + 2.31923076923077*U[18]*U[1] + 0.00172785135654124*U[22] - 1.66746568543758e-5*U[29]**3*U[31] - 0.00190568351656843*U[31] + 0.00557692307692308*U[4]\n", - "\tF[16] = 1.35185957626052*U[0]*U[15] - 0.00125723950739611*U[10]**3*U[16] - 0.421071015556554*U[11]*U[4] + 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 0.45*U[13]*U[7] - 1.90590038620335*U[14]*U[1] - 0.0230631483770408*U[15] - 0.0368649373881932*U[16] - 2.31923076923077*U[17]*U[1] - 2.31923076923077*U[18]*U[2] - 0.00557692307692308*U[26] + 3.92887346061283e-5*U[29]**3*U[35] + 0.00449016100178891*U[35] + 0.00557692307692308*U[5]\n", - "\tF[17] = -3.421202001397*U[0]*U[18] - 0.00102150709975934*U[10]**3*U[17] + 0.180063263231421*U[11]*U[7] - 0.7875*U[12]*U[5] - 0.7875*U[13]*U[4] + 0.288101221170274*U[14]*U[9] + 1.4625*U[15]*U[2] + 1.4625*U[16]*U[1] - 0.043546511627907*U[17] + 0.0374776161126912*U[18] - 5.47392320223521*U[20]*U[3] + 9.61561114513536e-5*U[21] - 2.70963173883608e-6*U[29]**3*U[30] - 0.000309673571442371*U[30] + 0.00725*U[6]\n", - "\tF[18] = 3.421202001397*U[0]*U[17] - 0.00102150709975934*U[10]**3*U[18] - 0.180063263231421*U[11]*U[6] + 0.7875*U[12]*U[4] - 0.7875*U[13]*U[5] - 0.288101221170275*U[14]*U[8] - 1.4625*U[15]*U[1] + 1.4625*U[16]*U[2] - 0.0374776161126912*U[17] - 0.043546511627907*U[18] + 5.4739232022352*U[19]*U[3] + 0.00725*U[7]\n", - "\tF[19] = -2.75575081119392*U[0]*U[20] - 0.000888267043268988*U[10]**3*U[19] - 0.250522801017629*U[11]*U[9] + 1.00209120407052*U[14]*U[7] - 4.00836481628207*U[18]*U[3] - 0.0473230535894843*U[19] + 0.0325892314023402*U[20] + 0.00024415290907648*U[22] - 2.35620151203137e-6*U[29]**3*U[31] - 0.000269281366471626*U[31] + 0.00819565217391304*U[8]\n", - "\tF[20] = 2.75575081119392*U[0]*U[19] - 0.000888267043268988*U[10]**3*U[20] + 0.250522801017629*U[11]*U[8] - 1.00209120407052*U[14]*U[6] + 4.00836481628207*U[17]*U[3] - 0.0325892314023402*U[19] - 0.0473230535894843*U[20] + 0.00819565217391304*U[9]\n", - "\tF[21] = 2.30554431681946e-7*U[12] + 2.5547312812995e-7*U[14] + 1.41879650265813e-7*U[17] - 2.30554431681946e-7*U[1] - 4.98594961275655e-7*U[21] - 0.000231547500016414*U[22]*U[25] - 0.000393263214313593*U[22]*U[27] - 0.00077917571434095*U[23]*U[26] - 0.00102174928578672*U[23]*U[28] - 0.00156202678582502*U[24]*U[27] - 2.49430228227486e-5*U[25] - 2.5547312812995e-7*U[3] - 1.41879650265813e-7*U[6]\n", - "\tF[22] = 1.27676499203106e-7*U[0] - 1.27676499203106e-7*U[11] + 4.43165421807242e-7*U[15] + 1.84356815471813e-7*U[19] - 0.000297563940911795*U[21]*U[25] + 0.00056941247952257*U[21]*U[27] - 1.45521268835226e-6*U[22] - 0.00185518259457354*U[23]*U[25] - 0.00363689261114416*U[24]*U[26] - 2.49312940023328e-5*U[26] - 4.43165421807242e-7*U[4] - 1.84356815471813e-7*U[8]\n", - "\tF[23] = -4.59275459509595e-7*U[14] - 0.00110122563125871*U[21]*U[26] + 0.0019601816236405*U[21]*U[28] + 0.000385428970940548*U[22]*U[25] - 3.04757790976247e-6*U[23] - 0.00547309138735578*U[24]*U[25] - 2.49117704610167e-5*U[27] + 4.59275459509595e-7*U[3]\n", - "\tF[24] = 5.09747214298137e-8*U[0] - 5.09747214298137e-8*U[11] - 0.0028417174170155*U[21]*U[27] - 0.000594010608460015*U[22]*U[26] + 0.00259237963074834*U[23]*U[25] - 5.2727039845599e-6*U[24] - 2.48844888171634e-5*U[28]\n", - "\tF[25] = -5.43087366736076e-7*U[13] + 0.000529111412123567*U[21]*U[22] + 2.49364240037345e-5*U[21] + 0.00146975392256546*U[22]*U[23] + 0.00288071768822831*U[23]*U[24] - 1.03680315467796e-6*U[25] + 5.43087366736076e-7*U[2]\n", - "\tF[26] = -1.04390781015316e-6*U[16] + 0.00188040062849988*U[21]*U[23] + 0.00423090141412473*U[22]*U[24] + 2.49247013868922e-5*U[22] - 1.99291491029239e-6*U[26] + 1.04390781015316e-6*U[5]\n", - "\tF[27] = -0.000176149545706673*U[21]*U[22] + 0.00440373864266684*U[21]*U[24] + 2.49051881654425e-5*U[23] - 3.58443843007766e-6*U[27]\n", - "\tF[28] = -0.000938435679208948*U[21]*U[23] + 2.4877920928742e-5*U[24] - 5.80838943890229e-6*U[28]\n", - "\tF[29] = 9.12059910499408e-6*U[10]**4 + 4.64878611115251e-5*U[10]**3*U[12] + 1.67415119447806e-5*U[10]**3*U[14] + 9.29757222230503e-6*U[10]**3*U[17] + 0.000521179401993356*U[10] + 0.000664115245477357*U[12] + 0.000239165516524779*U[14] + 0.000132823049095472*U[17] + 1.50119582264922*U[21]*U[35] - 0.750597911324609*U[21]*U[37] - 1.50119582264922*U[22]*U[34] + 1.12589686698692*U[22]*U[36] - 4.50358746794765*U[23]*U[35] + 2.25179373397383*U[23]*U[37] - 2.62709268963613*U[24]*U[34] - 5.62948433493457*U[24]*U[36] + 1.50119582264922*U[25]*U[31] + 2.62709268963613*U[25]*U[33] - 1.50119582264922*U[26]*U[30] + 4.50358746794765*U[26]*U[32] - 1.12589686698692*U[27]*U[31] + 5.62948433493457*U[27]*U[33] + 0.750597911324609*U[28]*U[30] - 2.25179373397383*U[28]*U[32] - 8.14339205803043e-7*U[29]**4 - 0.000260589700996678*U[29] + 5.70773716822425e-5\n", - "\tF[30] = -3.54406469365901e-21*U[10]**4 - 5.31652508387687e-5*U[10]**3*U[12] - 2.75103051082387e-5*U[10]**3*U[14] - 1.06330501677538e-5*U[10]**3*U[17] - 2.31450148821357e-19*U[10] - 0.000759506950146622*U[12] - 0.000393006100803065*U[14] - 0.000151901390029325*U[17] - 1.21682350103801*U[21]*U[35] + 0.608411750519007*U[21]*U[37] + 2.34182350103802*U[22]*U[34] - 1.28761762577851*U[22]*U[36] + 5.15047050311404*U[23]*U[35] - 2.57523525155702*U[23]*U[37] + 2.12944112681652*U[24]*U[34] + 6.43808812889255*U[24]*U[36] - 2.34182350103802*U[25]*U[31] - 2.12944112681652*U[25]*U[33] + 1.21682350103801*U[26]*U[30] - 5.15047050311404*U[26]*U[32] + 1.28761762577851*U[27]*U[31] - 6.43808812889255*U[27]*U[33] - 0.608411750519007*U[28]*U[30] + 2.57523525155702*U[28]*U[32] + 3.16434347648126e-22*U[29]**4 - 3.25735682321217e-6*U[29]**3*U[30] - 0.000260589700996678*U[30] - 2.21790142793497e-20\n", - "\tF[31] = 2.78802933171573e-5*U[10]**3*U[11] - 1.54836099362061e-5*U[10]**3*U[15] - 3.09672198724123e-6*U[10]**3*U[19] + 0.000398291670074584*U[11] - 0.000221195408173122*U[15] - 4.42390816346244e-5*U[19] - 1.125*U[21]*U[34] + 0.375*U[21]*U[36] + 1.875*U[23]*U[34] + 2.25*U[24]*U[35] + 1.125*U[25]*U[30] - 1.875*U[25]*U[32] - 2.25*U[26]*U[33] - 0.375*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[31] - 0.000260589700996678*U[31] + 4.36192251724445e-5\n", - "\tF[32] = -5.06294956237001e-22*U[10]**4 - 1.25605469675209e-5*U[10]**3*U[12] + 2.05688778355549e-5*U[10]**3*U[14] - 2.51210939350418e-6*U[10]**3*U[17] - 0.000179437180657833*U[12] + 0.000293842414478535*U[14] - 3.58874361315667e-5*U[17] - 1.90560783367934*U[21]*U[35] + 0.952803916839669*U[21]*U[37] - 1.46939216632066*U[22]*U[34] - 0.304205875259504*U[22]*U[36] + 1.21682350103801*U[23]*U[35] - 0.608411750519007*U[23]*U[37] + 3.33481370893884*U[24]*U[34] + 1.52102937629752*U[24]*U[36] + 1.46939216632066*U[25]*U[31] - 3.33481370893884*U[25]*U[33] + 1.90560783367934*U[26]*U[30] - 1.21682350103801*U[26]*U[32] + 0.304205875259504*U[27]*U[31] - 1.52102937629752*U[27]*U[33] - 0.952803916839669*U[28]*U[30] + 0.608411750519007*U[28]*U[32] + 4.52049068068751e-23*U[29]**4 + 3.61639254455001e-22*U[29]**3*U[30] - 3.25735682321217e-6*U[29]**3*U[32] - 0.000260589700996678*U[32] - 3.16843061133567e-21\n", - "\tF[33] = 1.11521173268629e-5*U[10]**3*U[11] + 0.000159316668029834*U[11] - 1.875*U[21]*U[36] - 2.25*U[22]*U[35] - 2.625*U[23]*U[34] + 2.625*U[25]*U[32] + 2.25*U[26]*U[31] + 1.875*U[27]*U[30] - 3.25735682321217e-6*U[29]**3*U[33] - 0.000260589700996678*U[33] + 1.74476900689778e-5\n", - "\tF[34] = 3.64823964199763e-5*U[10]**3*U[13] + 0.000521179401993355*U[13] + 1.125*U[21]*U[31] - 1.125*U[22]*U[30] + 1.875*U[22]*U[32] - 1.875*U[23]*U[31] + 2.625*U[23]*U[33] - 2.625*U[24]*U[32] - 3.25735682321217e-6*U[29]**3*U[34] - 0.000260589700996678*U[34]\n", - "\tF[35] = 3.64823964199763e-5*U[10]**3*U[16] + 0.000521179401993355*U[16] + 1.5*U[21]*U[32] + 2.25*U[22]*U[33] - 1.5*U[23]*U[30] - 2.25*U[24]*U[31] - 3.25735682321217e-6*U[29]**3*U[35] - 0.000260589700996678*U[35]\n", - "\tF[36] = -0.375*U[21]*U[31] + 1.875*U[21]*U[33] + 0.375*U[22]*U[30] - 1.875*U[24]*U[30] - 3.25735682321217e-6*U[29]**3*U[36] - 0.000260589700996678*U[36]\n", - "\tF[37] = -0.75*U[21]*U[32] + 0.75*U[23]*U[30] - 3.25735682321217e-6*U[29]**3*U[37] - 0.000260589700996678*U[37]\n", - "\treturn F" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e34a926e", - "metadata": {}, - "outputs": [], - "source": [ - "ten.tensor_dic" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b8fb285", - "metadata": {}, - "outputs": [], - "source": [ - "f(0, np.ones(38)) - f_num(0, np.ones(38))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cd4adc73", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", - "import matplotlib.pyplot as plt" + "def f_fix(t, U):\n", + " return f(t, U, eps, kd)" ] }, { @@ -835,7 +203,7 @@ "outputs": [], "source": [ "integrator = RungeKuttaIntegrator()\n", - "integrator.set_func(f)" + "integrator.set_func(f_fix)" ] }, { @@ -851,17 +219,6 @@ "integrator_num.set_func(f_num)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "6fa65680", - "metadata": {}, - "outputs": [], - "source": [ - "integrator_num = RungeKuttaIntegrator()\n", - "integrator_num.set_func(f_test)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -905,18 +262,6 @@ "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "b4a93eca", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator_num.integrate(0., 1000000., 0.1, ic=ic, write_steps=10)\n", - "reference_time_test, reference_traj_test = integrator_num.get_trajectories()" - ] - }, { "cell_type": "code", "execution_count": null, @@ -930,7 +275,6 @@ "\n", "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", - "plt.plot(reference_traj_test[varx], reference_traj_test[vary], marker='o', ms=0.07, ls='')\n", "\n", "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" @@ -947,7 +291,6 @@ "\n", "plt.plot(reference_traj[varx, :])\n", "plt.plot(reference_traj_num[varx, :])\n", - "plt.plot(reference_traj_test[varx, :])\n", "plt.show()" ] }, @@ -962,7 +305,6 @@ "\n", "plt.plot(reference_traj_num[vary, :])\n", "plt.plot(reference_traj[vary, :])\n", - "plt.plot(reference_traj_test[vary, :])\n", "plt.show()" ] }, @@ -977,7 +319,6 @@ "\n", "plt.plot(reference_traj_num[varx, ::1000])\n", "plt.plot(reference_traj[varx, ::1000])\n", - "plt.plot(reference_traj_test[varx, ::1000])\n", "plt.show()" ] }, @@ -991,7 +332,7 @@ "tendencies_sym = np.empty((38, reference_time[::1000].shape[0]))\n", "for n, x in enumerate(reference_time[::1000]):\n", " x = reference_traj_num[:, n]\n", - " tendencies_sym[:, n] = f(0, x)" + " tendencies_sym[:, n] = f_fix(0, x)" ] }, { diff --git a/qgs/functions/symbolic_tendencies.py b/qgs/functions/symbolic_tendencies.py index 4d332d4..18abdda 100644 --- a/qgs/functions/symbolic_tendencies.py +++ b/qgs/functions/symbolic_tendencies.py @@ -171,7 +171,7 @@ def create_symbolic_equations(params, atm_ip=None, ocn_ip=None, gnd_ip=None, con if return_jacobian: dict_eq_simplified = dict_eq - func = equation_as_function(equations=eq_simplified, params=params, language=language, string_output=True, + func = equation_as_function(equations=eq_simplified, params=params, language=language, continuation_variables=continuation_variables) if return_jacobian: @@ -316,7 +316,7 @@ def format_equations(equations, params, save_loc=None, language='python', print_ return equation_dict -def equation_as_function(equations, params, string_output=True, language='python', continuation_variables=None): +def equation_as_function(equations, params, language='python', continuation_variables=None): """Converts the symbolic equations to a function in string format in the language syntax specified, or a lambdified python function. @@ -326,8 +326,6 @@ def equation_as_function(equations, params, string_output=True, language='python Dictionary of the substituted symbolic model equations. params: QgParams The parameters fully specifying the model configuration. - string_output: bool - If `True`, returns a lambdified python function, if `False` returns a string function. Defaults to `True`. language: str Language syntax that the equations are returned in. Options are: - `python` @@ -342,8 +340,7 @@ def equation_as_function(equations, params, string_output=True, language='python Returns ------- f_output: callable or str - If string_output is `True`, output is a function in the specified language syntax, if `False` the output is - a lambdified python function. + Output is a function as a string in the specified language syntax. """ if continuation_variables is None: @@ -353,41 +350,36 @@ def equation_as_function(equations, params, string_output=True, language='python f_output = list() if language == 'python': - if string_output: + f_output.append('@njit') + func_def_str = 'def f(t, U' + for v in continuation_variables: + func_def_str += ', ' + str(v.symbol) - f_output.append('def f(t, U, **kwargs):') - f_output.append('\t#Tendency function of the qgs model') - f_output.append('\tF = np.empty_like(U)') + f_output.append(func_def_str + '):' ) - for v in continuation_variables: - f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") + f_output.append('\t# Tendency function of the qgs model') + for v in continuation_variables: + f_output.append('\t# ' + str(v.symbol) + ":\t" + str(v.description)) - for n, eq in eq_dict.items(): - f_output.append('\tF['+str(n-1)+'] = ' + str(eq)) - - f_output.append('\treturn F') - f_output = '\n'.join(f_output) - else: - # Return a lambdified function - vec = [Symbol('U['+str(i-1)+']') for i in range(1, params.ndim+1)] - sorted_dict = dict(sorted(eq_dict.items())) - array_eqs = np.array(list(sorted_dict.values())) - inputs = ['t', vec] + f_output.append('') + f_output.append('\tF = np.empty_like(U)') - for v in continuation_variables: - inputs.append(v.symbol) + for n, eq in eq_dict.items(): + f_output.append('\tF['+str(n-1)+'] = ' + str(eq)) - f_output = lambdify(inputs, array_eqs) + f_output.append('\treturn F') + f_output = '\n'.join(f_output) if language == 'julia': eq_dict = translate_equations(eq_dict, language='julia') f_output.append('function f!(du, U, p, t)') - f_output.append('\t#Tendency function of the qgs model') + f_output.append('\t# Tendency function of the qgs model') - for v in continuation_variables: - f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") + for i, v in enumerate(continuation_variables): + f_output.append('\t' + str(v.symbol) + " = p[" + str(i+1) + "] " + "\t# " + str(v.description)) + f_output.append('') for n, eq in eq_dict.items(): f_output.append('\tdu['+str(n)+'] = ' + str(eq)) @@ -398,20 +390,17 @@ def equation_as_function(equations, params, string_output=True, language='python eq_dict = translate_equations(eq_dict, language='fortran') f_var = '' - if len(continuation_variables) > 0: - for fv in continuation_variables: - f_var += str(fv.symbol) + ', ' - f_output.append('SUBROUTINE FUNC(NDIM, t, U, F, ' + f_var[:-2] + ')') - else: - f_output.append('SUBROUTINE FUNC(NDIM, t, U, F)') + for fv in continuation_variables: + f_var += ', ' + str(fv.symbol) + f_output.append('SUBROUTINE FUNC(NDIM, t, U, F' + f_var + ')') - f_output.append('\t!Tendency function of the qgs model') + f_output.append('\t! Tendency function of the qgs model') f_output.append('\tINTEGER, INTENT(IN) :: NDIM') f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*)') f_output.append('\tDOUBLE PRECISION, INTENT(OUT) :: F(NDIM)') for v in continuation_variables: - f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: ' + str(v.symbol)) + f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: ' + str(v.symbol) + "\t! " + str(v.description)) f_output.append('') @@ -466,8 +455,7 @@ def jacobian_as_function(equations, params, language='python', continuation_vari Returns ------- f_output: callable or str - If string_output is `True`, output is a function in the specified language syntax, if `False` the output is - a lambdified python function. + Output is a function as a string in the specified language syntax """ if continuation_variables is None: @@ -477,13 +465,20 @@ def jacobian_as_function(equations, params, language='python', continuation_vari f_output = list() if language == 'python': - f_output.append('def jac(t, U, **kwargs):') - f_output.append('\t#Jacobian function of the qgs model') - f_output.append('\tJ = np.zeros((len(U), len(U)))') + f_output.append('@njit') + func_def_str = 'def jac(t, U' + for v in continuation_variables: + func_def_str += ', ' + str(v.symbol) + + f_output.append(func_def_str + '):') + + f_output.append('\t# Jacobian function of the qgs model') for v in continuation_variables: - f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") + f_output.append('\t# ' + str(v.symbol) + ":\t" + str(v.description)) + f_output.append('') + f_output.append('\tJ = np.zeros((len(U), len(U)))') for n, eq in eq_dict.items(): f_output.append('\tJ[' + str(n[0] - 1) + ', ' + str(n[1] - 1) + '] = ' + str(eq)) @@ -494,11 +489,12 @@ def jacobian_as_function(equations, params, language='python', continuation_vari eq_dict = translate_equations(eq_dict, language='julia') f_output.append('function jac!(du, U, p, t)') - f_output.append('\t#Jacobian function of the qgs model') + f_output.append('\t# Jacobian function of the qgs model') - for v in continuation_variables: - f_output.append('\t' + str(v.symbol) + " = kwargs['" + str(v.symbol) + "']") + for i, v in enumerate(continuation_variables): + f_output.append('\t' + str(v.symbol) + " = p[" + str(i+1) + "]") + f_output.append('') for n, eq in eq_dict.items(): f_output.append('\tdu[' + str(n[0]) + ', ' + str(n[1]) + '] = ' + str(eq)) @@ -509,24 +505,22 @@ def jacobian_as_function(equations, params, language='python', continuation_vari eq_dict = translate_equations(eq_dict, language='fortran') f_var = '' - if len(continuation_variables) > 0: - for fv in continuation_variables: - f_var += str(fv.symbol) + ', ' - f_output.append('SUBROUTINE FUNC(NDIM, t, U, JAC, ' + f_var[:-2] + ')') - else: - f_output.append('SUBROUTINE FUNC(NDIM, t, U, JAC)') - f_output.append('\t!Jacobian function of the qgs model') + for fv in continuation_variables: + f_var += ', ' + str(fv.symbol) + f_output.append('SUBROUTINE FUNC(NDIM, t, U, JAC' + f_var + ')') + + f_output.append('\t! Jacobian function of the qgs model') f_output.append('\tINTEGER, INTENT(IN) :: NDIM') f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: U(NDIM), PAR(*)') - f_output.append('\tDOUBLE PRECISION, INTENT(OUT) :: JAC(NDIM)') + f_output.append('\tDOUBLE PRECISION, INTENT(OUT) :: JAC(NDIM, NDIM)') for v in continuation_variables: - f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: ' + str(v.symbol)) + f_output.append('\tDOUBLE PRECISION, INTENT(IN) :: ' + str(v.symbol) + "\t! " + str(v.description)) f_output.append('') - f_output = _split_equations(eq_dict, f_output) + f_output = _split_equations(eq_dict, f_output, two_dim=True) f_output.append('END SUBROUTINE') f_output = '\n'.join(f_output) From 02d4bf6b95540dd6d21033c558b686842dd35155 Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 28 Feb 2024 14:46:21 +0100 Subject: [PATCH 127/143] Cleanup of notebook --- notebooks/Symbolic Output-Linear-ground-AUTO.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb index 0bf2859..5c0cc53 100644 --- a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb +++ b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb @@ -717,7 +717,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.16" + "version": "3.8.10" } }, "nbformat": 4, From 2ce9cd048957353d23388f0200d010d60a1ed133 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 4 Jun 2024 12:11:15 +0200 Subject: [PATCH 128/143] Symbolic outputs documentation --- .../source/files/technical_description.rst | 16 ++++++++ documentation/source/files/user_guide.rst | 38 ++++++++++++++++--- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/documentation/source/files/technical_description.rst b/documentation/source/files/technical_description.rst index 75ffe6e..7270c02 100644 --- a/documentation/source/files/technical_description.rst +++ b/documentation/source/files/technical_description.rst @@ -94,6 +94,20 @@ The computational flow is as follows: Sketch of the computational flow. + +Generating Symbolic Equations +----------------------------- +Using `Sympy`_ qgs offers the functionality to return the ordinary differential equations of the projected model as a string, with any parameters the user chooses to be returned as varibales in the equations. In addition, the resulting equations can be returned already formatted in the programming language of the users' choice. This allows the qgs framework to feed directly into pipelines in other programming languages. Currently the framework can return the model equations in the following languages: + +* Python +* Julia +* Fortran-90 +* Mathematica +* AUTO-p07 continuation software + +This also allows the user to specify their own integration method for solving the model equations in python. + + Additional technical information -------------------------------- @@ -103,6 +117,8 @@ Additional technical information * qgs has a `tangent linear model`_ optimized to run ensembles of initial conditions as well, with a broadcast integration of the tangent model thanks to `Numpy`_. +* The symbolic output functionality of the qgs model relies on `Sympy`_ to perform the tensor calculations. This library is significantly slower than the numerical equivilent and as a result it is currently only feasible to generate the symbolic model equations for model resolutions up to :math:`4x4`. + References ---------- diff --git a/documentation/source/files/user_guide.rst b/documentation/source/files/user_guide.rst index 390d57a..f73e18a 100644 --- a/documentation/source/files/user_guide.rst +++ b/documentation/source/files/user_guide.rst @@ -462,7 +462,33 @@ This concludes the initialization of qgs, the function :meth:`f` hence produced An example of the construction exposed here, along with plots of the trajectories generated, can be found in the section :ref:`files/examples/manual:Manual setting of the basis`. See also the following section for the possible usages. -4. Using qgs (once configured) + +4. Symbolic outputs +------------------- + +If the user wants to generate the model tendencies with non-fixed parameters, use the tendencies in another programming language, or use their own integrator in python, the qgs framework can use `Sympy`_ to proform the above calculations symbolically rather than numerically. + +To return the tendencies including specified parameter values, or to format the tendencies in another programming language, the qgs model is configured as shown in section :ref:`files/user_guide:Configuration of qgs`, however the **Symbolic** inner product is always used in this pipeline. To create the symbolic tendencies, the instance of :class:`.QgParams` is passed to the function :func:`.create_symbolic_tendencies`: + +.. code:: ipython3 + + from qgs.functions.symbolic_tendencies import create_symbolic_equations + + parameters = [model_parameters.par1, model_parameters.par2, model_parameters.par3] + + f = create_symbolic_equations(model_parameters, continuation_variables=parameters, language='python') + +The varibale :meth:`f` is a string of the model tendencies, formatted in the programming language specified by the keyword :meth:`language`. The model tendencies will contain the specified :meth:`continuation_variables` as free parameters. + +Currently the framework can format the equations in the following programming languages: +* :meth:`python` +* :meth:`julia` +* :meth:`fortran` +* :meth:`mathematica` +* :meth:`auto` + + +1. Using qgs (once configured) --------------------------------- Once the function :math:`\boldsymbol{f}` giving the model's tendencies has been obtained, it is possible to use it with @@ -482,7 +508,7 @@ the qgs built-in integrator to obtain the model's trajectories: Note that it is also possible to use other ordinary differential equations integrators available on the market, see for instance the :ref:`files/examples/diffeq_example:Example of DiffEqPy usage`. -4.1 Analyzing the model's output with diagnostics +5.1 Analyzing the model's output with diagnostics ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. include:: diagnostic.rst @@ -524,7 +550,7 @@ Note that it is also possible to use other ordinary differential equations integ More diagnostics will be implemented soon. -4.2 Toolbox +5.2 Toolbox ^^^^^^^^^^^ The toolbox regroups submodules to make specific analysis with the model and are available in the :mod:`qgs.toolbox` module. @@ -536,7 +562,7 @@ Presently, the list of submodules available is the following: More submodules will be implemented soon. -4.2.1 Lyapunov toolbox +5.2.1 Lyapunov toolbox """""""""""""""""""""" This module allows you to integrate the model and simultaneously obtain the *local* `Lyapunov vectors`_ and `exponents`_. @@ -555,10 +581,10 @@ See also :cite:`user-KP2012` for a description of these methods. Some example notebooks on how to use this module are available in the `notebooks/lyapunov <../../../../notebooks/lyapunov>`_ folder. -5. Developers information +6. Developers information ------------------------- -5.1 Running the test +6.1 Running the test ^^^^^^^^^^^^^^^^^^^^^ The model core tensors can be tested by running `pytest`_ in the main folder: :: From 17c8033ca2757e2e3f5ce95330ae86042fa8d73d Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 7 Oct 2024 10:39:08 +0200 Subject: [PATCH 129/143] intro notebook added --- notebooks/introduction_qgs.ipynb | 730 +++++++++++++++++++++++++++++++ 1 file changed, 730 insertions(+) create mode 100644 notebooks/introduction_qgs.ipynb diff --git a/notebooks/introduction_qgs.ipynb b/notebooks/introduction_qgs.ipynb new file mode 100644 index 0000000..f93fbe8 --- /dev/null +++ b/notebooks/introduction_qgs.ipynb @@ -0,0 +1,730 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to qgs tutorial" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook is a tutorial in which you will learn:\n", + "\n", + "1. How to install the [qgs](https://github.com/Climdyn/qgs) framework for low-order climate and weather modeling\n", + "2. How to run the model\n", + "3. Some details about how the model works inside\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is a simple introduction to the model, only to show how to use the model and to allow you to play a bit with the model parameters." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you run this notebook, remember that a full documentation is available online to help you as well on\n", + "\n", + "> [qgs.readthedocs.io](https://qgs.readthedocs.io/en/latest/)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Model & tutorial installation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If it not yet done, you will instructions here on how to install qgs." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### With pip" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The easiest way to install and run qgs is to use [pip](https://pypi.org/). Simply uncomment and run the line below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#!pip install qgs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and you are set!" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### With Anaconda" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To install qgs and this tutorial, you will need first to install [Anaconda](https://www.anaconda.com/) on your laptop. Then clone the qgs repository and follow the [installation instructions](https://github.com/Climdyn/qgs#installation). You can then copy this tutorial notebook in the **root folder** of qgs and you are ready." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Running the model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using qgs, we will now define a simple 2-layer channel QG atmosphere truncated at wavenumber 2 on a beta-plane with a simple orography (a montain and a valley). Subsequently, we will integrate this model and plot its trajectories.\n", + "\n", + "More details on this model version can be found in the articles:\n", + "* Reinhold, B. B., & Pierrehumbert, R. T. (1982). *Dynamics of weather regimes: Quasi-stationary waves and blocking*. Monthly Weather Review, **110** (9), 1105-1145. [doi:10.1175/1520-0493(1982)110%3C1105:DOWRQS%3E2.0.CO;2](https://doi.org/10.1175/1520-0493(1982)110%3C1105:DOWRQS%3E2.0.CO;2)\n", + "\n", + "or in the [documentation](https://qgs.readthedocs.io/en/latest/files/model/oro_model.html)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**To start, simply run the current notebook and follow the cells down to the bottom.**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Modules import" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, load some modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "sys.path.extend([os.path.abspath('../')])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Importing the model's modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.params.params import QgParams\n", + "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", + "from qgs.functions.tendencies import create_tendencies\n", + "from qgs.plotting.util import std_plot" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and diagnostics" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.diagnostics.streamfunctions import MiddleAtmosphericStreamfunctionDiagnostic\n", + "from qgs.diagnostics.variables import VariablesDiagnostic\n", + "from qgs.diagnostics.multi import MultiDiagnostic" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Defining the model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we define some general parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Time increment parameter\n", + "dt = 0.1\n", + "# Saving the model state every 5 steps\n", + "write_steps = 5\n", + "# transient time\n", + "transient_time = 200000.\n", + "# integration time\n", + "integration_time = 100000." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we will create the model parameters object. It is an object which entirely define the model version and parameters. This object will be use by qgs to construct the tendencies $\\boldsymbol{f}$ of the model." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters = QgParams()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now setup some parameters with the [`set_params`](https://qgs.readthedocs.io/en/latest/files/technical/configuration.html#qgs.params.params.Params.set_params) method of the parameters object:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# here we define the latitude to be 50 degrees and a predefined amplitude of the meridional temperature gradient\n", + "model_parameters.set_params({'phi0_npi': np.deg2rad(50.)/np.pi, 'hd':0.045})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and indicate that we want an atmospheric channel for the atmosphere, with Fourier modes up to wavenumber 2 in each spatial direction (directions $x$ and $y$, which will be clarified below):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.set_atmospheric_channel_fourier_modes(2, 2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We also set some topography" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.ground_params.set_orography(0.2, 1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and indicate the amplitude of the meridional temperature gradient which forces the model:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.atemperature_params.set_thetas(0.1, 0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and we are done configuring the model. We can now print the parameters to check what we have done:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters.print_params()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We are now ready to feed the parameters object to qgs to get the tendencies $\\boldsymbol{f}$ that will allow us to integrate the differential equation $\\dot{\\boldsymbol{x}} = \\boldsymbol{f}(\\boldsymbol{x})$" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "f, Df = create_tendencies(model_parameters)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Time integration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now integrate our model with an integrator, an algorithm that will give us the solution of the differential equation above. There are many options for doing that, but here we will use qgs built-in integrator (a [Runge-Kutta 4th-order integrator](https://en.wikipedia.org/wiki/Runge%E2%80%93Kutta_methods)):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "integrator = RungeKuttaIntegrator()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will tell this integrator to use our freshly defined model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "integrator.set_func(f)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now start from a small random initial condition and integrate over a transient time to obtain an initial condition on the model attractor:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "ic = np.random.rand(model_parameters.ndim)*0.1\n", + "integrator.integrate(0., transient_time, dt, ic=ic, write_steps=0) # write_steps=0 will only give us the last step\n", + "time, ic = integrator.get_trajectories()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have obtained an initial condition on the attractor, we can integrate further to obtain a **trajectory** on the attractor" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%time\n", + "integrator.integrate(0., integration_time, dt, ic=ic, write_steps=write_steps)\n", + "time, trajectory = integrator.get_trajectories()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Plotting the trajectory of the model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The typical time evolution of a model variable as a function of time can now be plotted:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "var = 1\n", + "plt.figure(figsize=(10, 8))\n", + "\n", + "plt.plot(model_parameters.dimensional_time*time, trajectory[var])\n", + "\n", + "plt.xlabel('time (days)')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[var]+'$');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also plot for example the 2nd and 3rd variables in a plane." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "varx = 2\n", + "vary = 1\n", + "plt.figure(figsize=(10, 8))\n", + "\n", + "plt.plot(trajectory[varx], trajectory[vary], marker='o', ms=0.07, ls='')\n", + "\n", + "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This show a 2-dimensional projection of the [attractor](https://en.wikipedia.org/wiki/Attractor) of the model, i.e. the set of states toward which the model tends to evolve, for a wide variety of starting conditions of the system." + ] + }, + { + "attachments": { + "image.png": { + "image/png": "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LX4v7ScimqtXMioazjqKCBQ9AZwWKpI6d+7s6tevbyo6VOhsUSejgHnhhRe6Bg0a2KYlAiIgAiJQ2gSoREKQQFsHFOgozREryEaU9nOhqxcBERCBOBCg3RCCCQJsBPsQ9dFhgkpeLREQAREQAREoFAHsE3aJGVHYKRJTzIYnOVWIJBAVWsyv0ko2ASWjkn1/dXUiEBgBFH1stJRAoUCFFKWzjRs3DuwY2pEIiIAIiIAI5EOAitk6deqYem7EiBGmpiMhddddd+WzO71HBERABERABAIjgGBi8ODBNi+KtucE/Jgj2KRJk8COoR2JgAiIgAiIQK4ESP4wm4lKXeJ8jMBAfI7d0hKBsAhUO90r+POwdq79ioAIJIvAli1b3IwZM2zgLZVSKBa6du1aEMVEssjqakRABERABKpKgK+0qPjY+DtOFIo+PzS+qvvX+0VABERABEQgXwKIJLBP/ImAgo4TbFHP5Mj3/PU+ERABERCB5BH47LPP3Pz5893ChQstKVWvXj1rcU67cdmn5N3vuFyRklFxuRM6DxEQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREQARFIIIHzEnhNuiQREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREIGYEFAyKiY3QqchAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAkkkoGRUEu+qrkkEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEYkJAyaiY3AidhgiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAgkkYCSUUm8q7omERABERABERABERABERABERABERABERABERABERABERABEYgJASWjYnIjdBoiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIikEQCSkYl8a7qmkRABERABERABERABERABERABERABERABERABERABERABEQgJgTOj8l56DREQAREQAREQAREQAREQAREQAREQAREQAREQAREQAREoOAEPv/8c3fixAl33nnnuerVq4d+Phzv5MmTdhyOV61atdCPqQOIQNQElIyKmriOV3ACGJJPPvnEDEpF67LLLnMXXnhh1h/+3kixX94XhaE6deqUO3r0qLvgggts0xIBERABEShuAp9++qk7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G6ZcEmDZnpteJwIiIAIiEG8CCCXwc0j6ZBLqeR8K5Tk2JRcfivdio+RDxfsZ0NmJgAiIQNwIYJfwnWijh53CjlChRCVtehyPKlySVZdffrnZsmxXvj4U78PXwsYhVs9mPmO256TXiUBcCCgZFZc7ofOINQEMEO2GWCSHUEukJ4ZIKtHXFcOGuo7ZHWEvgn0krJgbkqn9RdjH1/5FQAREQAQKTwCnBXuAs0KwL1ObWCpwEVdEZS+wm/Rkx5HCcct0ToUnpzMQAREQAREoNAH5UIW+Azq+CIiACJQeAXwU4ndsiOqI4SnxU3rPga64MASUjCoMdx1VBERABERABERABERABERABERABERABERABERABERABERABEqCgGZGlcRtTt5F0sJu2LBhpvDev3+/tbGjH/iAAQOSd7G6IhEQAREQgaIi8LOf/cz6j99www2O3uDMw+jXr5+1VNUSAREQAREQgUIRYAj766+/Lh+qUDdAxxUBERABESiXgHyoctHoFyKQKAKqjErU7Uz2xVBGS8sfZl5QSsscio4dO9rsi/fff98xrJZ+49ddd50NomU+BT1WmaFBe6D0wYDJpqWrEwEREAERiJIAbfCwRcwO7N27t3vppZfciy++aMmo9evXm32iJ3mHDh2sL3mnTp3MNmGjaOuaPlw9ynPXsURABERABJJLINWHor03sy/at29vg9qxW2vWrDHbde2117omTZrIh0ruo6ArEwEREIHYEUj1oXr16uVefvll+VCxu0s6IREIloCSUcHy1N5CJLBv3z5LODEbAwdqwoQJ5jClD2LndRs3brSNwe4NGjSwGU+NGjWyuUp+i2KmU4g4tGsREAEREIGYECC4R0Bvw4YNVgV16623uu9///vmSKUvBBXYsXnz5pk9qlOnjgUAEU/QqzyqmU7p56V/i4AIiIAIJJNAJh8K4R7JqNRFtwnsmHyoZD4HuioREAERiBsBROZ0k/A+1C233OJ+8IMfZPShFi5c6I4ePXqOD3XVVVfZfFr8KM1Rj9sd1vmIQGYCatOXmYt+GjMCKPY2bdpkRqayVnxURrF17tzZrsInsObMmXMmEUWw7/LLL7f9sWHAtERABERABEQgVwJlZWVu5cqVVgHVvXt3d80111S4C8QUrG7durlDhw5ZEmv79u3miOFEUcXLn6n2iUoqLREQAREQARHIlcDa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IiACnxFgIw1nD70vNm/e7AqKCHFKOyRaqIeOQUckPCWTUJ4Yd+PHj0/0UHQ/ERABERCBJBPAYKEEBL0vLl686H0IyWIiyzYZMm7cOFuxYoVt3LjR9eSFCxe8F0dlPRCTMT7dUwREQAREIHEEsKHYSEslG4rehrKhEvcZ0J1EQAREIBUJYEOxl5YqNhQB79hv69evd8cYfXv5PRl7jqm4Xpkypno/CiRTJqN5iIAIxJcAG3woBTbVaHR74MAB27p1qyuJyN4Y8R3Fvatj0IV9pih1wZhogshYJCIgAiIgAtlFgBJ46CSyd8lKouTQ0qVLHUIyHFKU9evTp4/3P6QpPAEUZBXn5+dn18JotiIgAiIgAh4kEWlDHTx40LZs2SIbSp8NERABERCBpBLAhqKXU6rYUJRYx3ajfxWOqMaNG9uUKVMSvt+Y1EXJgpsrMyoLFllTFIFYEUAxEDnx5JNPeqQ3TQ7feustw6CiLFKihU29eJS4SPQ8dD8REAEREIG6ESBrl9J56KLZs2d7dB8OIRxS06ZNq9vFa/lu9ORzzz1Xy3frbSIgAiIgAplCABuKckMVbSiCKGRDZcoqax4iIAIikH4EIm2oWbNmeeBcsm2orl272ksvvZR+MDXiqAmouUrUqHSiCIgATQcxpk6dOuVOKfph8MjzEhEQAREQARFIFgGMJjJ279y543XFKTdx6dIlD5yQiIAIiIAIiEAyCWArETAhGyqZq6B7i4AIiIAIVCSADUWVI3qx059JNlRFQvo9HgRyggidsnhcWNcUARHITAKrVq0yyuIhlMojaoHeGHJIZeZ6a1YiIAIikC4EKNdKnyaa8CL169f3rCj0lEQEREAEREAEkklg9erVtnfvXh+CbKhkroTuLQIiIAIiEEmgMhvq0UcfTUrmrlYmOwjIGZUd66xZikDMCNCHg4gJHomiII2XOq4YVRIREAEREAERSBaBkpISj+a7deuWl0MiCp1sKcpPSERABERABEQgmQRkQyWTvu4tAiIgAiJQFQGqHWE/yYaqipCejzUBOaNiTVTXEwEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAEREAERKCegUFF9GERABERABERABERABERABERABERABERABERABERABERABERABOJGQM6ouKHVhUVABERABERABERABERABERABERABERABERABERABERABERABOSM0mdABERABERABERABERABERABERABERABERABERABERABERABEQgbgTkjIobWl1YBERABERABERABERABERABERABERABERABERABERABERABERAzih9BkRABERABERABERABERABERABERABERABERABERABERABERABOJGQM6ouKHVhUVABERABERABERABERABERABERABERABERABERABERABERABOSM0mdABERABERABERABERABERABERABERABERABERABERABERABEQgbgTkjIobWl1YBERABERABERABERABERABERABERABERABERABERABERABESgvhCIQLYRKCkpsbt371ppaWm1U2/YsKHVq1fPcnJyqj0vfLGsrMy4No+8Lzc3/r5e5lBcXOz345CIgAiIgAikFwG+x9FJ6I/qhO949FK0Oqm6a6GnuC/3rF+/fkL0FbqKe0pfVbcyek0EREAEREAEREAEREAEREAEREAEMpeAnFGZu7aaWRUELl++bOvWrbNz585Vcca9p2fMmGHdunWr9pzIF4uKiuzgwYN27do1GzhwoDVv3jzq99b2RDb3PvnkE+vatav169dPDqnagtT7REAERCBJBG7evOk66fjx49WOoHfv3jZ+/Hh3SNVV0B2HDh2yS5cuub5q2bJlXS/50Pejc3fs2GGdOnXyeyqA4qHIdIIIiIAIiIAIiIAIiIAIiIAIiIAIZBQBOaMyajk1mWgI3Lp1y3bt2mUHDhyo9vQRI0ZE7Ywiunzfvn329ttvW7t27YxNw0QI9122bJnl5+fb888/b3369EnEbXUPERABERCBGBG4c+eO7d2717Zt21btFcmIGjNmTLXnRPMieoPAid///vfWrFkz69WrVzRvq/M5jH/58uV+z9atW7tTSiICIiACIiACIiACIiACIiACIiACIpA9BOSMyp611kwrIdC9e3cbNmyYlymqKB07dqz4VJW/37592zZs2GDnz5+3cePGJSQrisE0aNDAJk2aZO+//75t2rTJnWd5eXlVjlMviIAIiIAIpC4BvtPnzp1badYQzhter6uQxYu+On36tOu/Vq1a1fWSUb2/ffv29sgjj9jChQtty5Yt1qZNm5hkeUV1c50kAiIgAiIgAiIgAiIgAiIgAiIgAiKQdAIP7sAnfUgagAgkjgDl7WbPnl2nDTF6b1y5csWzrXBg9e3b1x1ClECiJCDCZh/R50SkHzt2zEsjIcOHD3/AEXb9+nX78MMP7eLFi94b5Bvf+EY5EDYPV61a5e8fNGiQTZ482YYMGWJr1qyxI0eO2KlTp6xHjx7l5+sHERABERCB9CFACT50UjyDCtAxlMvDOdS/f3+/F/oDvYI+a9GihWfZoq9OnDhhFy5ccIAdOnSwzp073weTHlA4lzgP+eY3v1n++tmzZ2316tUepDFgwAB3RPH41ltveRYY905UFvF9g9YvIiACIiACIiACIiACIiACIiACIiACSSEgZ1RSsOummUaAUnlXr151B1GXLl18emRLUQaJxvQ4vb773e/ayZMnPYsJh1Tjxo09Kr2i5Obm+sbe7t27/aWnnnrKNwERnF5sIuKU4jmcVfSmImJ+586d/hrPN2rUqOJl9bsIiIAIiIAI2NKlSz1QggCJsC8ipQLnz59vlLElY+nrX/+667AFCxbY4cOHXad861vfeoAeOgi9RmYujiwCIsLye+hEdBL6rG3btq6vmjZt6qVs6Y919OhROaMeIKonREAEREAEREAEREAEREAEREAERCBzCcgZlblrq5lFQYBNsiVLljyQncRmGhlO0Th12IDbuHGjb7QRYR6WUerXr59HmNObinM+/vhj36jDacQ5X/nKVx64L0PGSUW2Ez092CBcv369O6QQNhDZLEQ4B6HEIBt8NKRng+/GjRtRjdvfLBEBERABEUgZAnzno5Pq1at335gIciBbicypusqnn37ql0BfhRlYXJt+VO+++64HUKCn0GlkMJH99IUvfMEzqSoK55DhRGYwumndunX27LPP+mk4o27evOk/E6iBoFPJhlq7dq1du3bN7xXPLDC/qUQEREAEREAEREAEREAEREAEREAERCAlCMgZlRLLoEEkiwCliTgqCn2YyGaK1hkVOoAie2/gJHrxxRft5z//uTuhiDDHCcXG3vjx472cHht5FYXMqEcffdRL9bExyebe9OnT/VwcW2zwjRo1qjwDi428goICvwy9QLi+RAREQAREIP0I8B3+zjvvPDDwqVOnuk4KnVEEOHAg6IbKdMkDFwme4D3oK64Tqa9wfo0dO9a2bt3qmbsffPCBZ92iTwisIIOqst6K3AN9uWLFCn8fJWNnzZrlzjT0FQEUlKNl7Aj6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xUHQN4ojFK5Nbyiye8Pr8RplaznC7F4yquhXFXkO7+V1elgxZwJDKgZgVLy/fhcBERABERABERABERABERABERABEcgcAirTlzlrqZnEgQDlidatW+c9mig/RP8LosMpuxc6jkIHE84iMp0o5xcZlf6wYbGBRz8OnE+RJYsWLVpkb731ljup2Czk+u3atXvgcmw6Mk4cXGR7aXPvAUR6QgREQASyggAZTeiD7du3exk+SuKhv8jADUv0hbqCvk5k3dZEX3Hujh07vCwtGcGhEKTx+9//3g+E3lSUua0o6E/GR3+pyJJ+Fc/T7yIgAiIgAiIgAiIgAiIgAiIgAiIgAplHQJlRmbemmlEMCbCJh4Np9erVvplHpDn9mYgsD8sYcbuwV8YHH3zgJY2IJK/Y26mqYZH5xKbhnDlzvARSKJs2bfIeVDii2LjjYDyRggOLseHEImuqYn+Oqu6p50VABERABNKXALqA4AjK4EXqhXnz5tmvf/1re/fdd90hdfLkSQ9UoGReWP4PPUbWEn2gcEZRPpbM2miE4Akym8guHjJkSPlbuM+KFStcFxIUUZm+wgnFOegpXieoQyICIiACIiACIiACIiACIiACIiACIpA9BOr9KJDsma5mKgI1JxD28aC8EBttY8eO9ebukSWG2ORjMw/nEBt9vCfaXhhsJJL1FDq7whGS4dSjRw/v3zFp0iR/vWLW04ULFzxKnc2/kSNHRn3PmlPQO0RABERABFKFADoHZxS6g16BoaOJjCScPJTHQx+hG3AcEegQlszjEX1FIAT6hwynaIMnuA/XRDeh6yKF++L4mjJlygMlZTnv0qVLtmXLFnd+jRo1Kup7pgpzjUMEREAEREAEREAEREAEREAEREAERKBuBHKCGv5ldbuE3i0CmU+A3hoXL170zTx6ToXljiJnTvkiSubhtCJTqbJzYk2KkkccbCwqyjzWdHU9ERABEUg/AvxZh16glB5BEwRGVAxkQF/duHHDe0ah0yL7FcZrxqG+on8V95SIgAiIgAiIgAiIgAiIgAiIgAiIgAhkFwE5o7JrvTVbERABERABERABERABERABERABERABERABERABERABERABEUgoAfWMSihu3SxWBOiFQU8M+ilR+odyRTRDnzp1aqxuoeuIgAiIgAiIQK0I/OIXv7Bjx45Zu3btPAMpslxerS6oN4mACIiACIiACIiACIiACIiACIiACIhAmhNQZlSaL2A2DZ/SQ/TAWLdunZfMO3funE2cONF7VOzfv9+btS9evNh7K9E/iebq9MJAKFEU9svIJmaaqwiIgAiIQGIIoKOOHz9u77//vs2cOdN++ctf2j/8wz/Y1atXbefOnfbRRx/ZqVOnbNasWV4677HHHvOBoZ/CIzEj1V1EQAREQAREQAREQAREQAREQAREQAREIPEE5IxKPHPdsZYE6Me0bds2u3DhgjdIX7JkiQ0YMMCdUZFy5swZ27Vrl+3Zs8dOnz7trzdq1MjGjh3rfTHo5cRj2PC9lsPR20RABERABETACdy9e9cdURs2bLChQ4d6pu7f/u3fujOqoqxYscJ7Cy5fvtx7J/Xv39+6devmGb7op1BHVXyffhcBERABERABERABERABERABERABERCBdCagMn3pvHpZNHbKHW3dutWdSE8//XS1M+/QoYNxzJgxw8/bsmWLb/y98cYb1qZNG2vbtq21bt3aNwHz8/OtWbNmRkN1iQiIgAiIgAjUlMDNmzc9AOLEiRM2ffp0KygoqPYSjzzyiL8+e/Zsu3z5su3bt892795tn376qesn9BQ6Ct0U6igcVBIREAEREAEREAEREAEREAEREAEREAERSGcCckal8+plwdjZ5MMRRZZTnz59POK8pjJixAh/y5gxY7xEEgcR7AcPHvSeUzi4yJxi8y88eF4iAiIgAiIgAlURKC0ttfPnz3uZWDKj6FmII6km0qpVKxs/frwfOKZCHUUQBXop1FE4piJ1VE3uoXNFQAREQAREQAREQAREQAREQAREQAREIBUIyBmVCqugMVRKAEfU5s2b7cqVK94bimynukj9+vW9FBIHQlN5NhIp+0cPKpxehw8ftuLiYmvfvr1v/PXq1cudVRIREAEREAERiCRASdj169e7bho1apT3gaqL4JjiGDx4sOHoQjdF6ij6JKKfSkpKvAwg9+vevXtdbqn3ioAIiIAIiIAIiIAIiIAIiIAIiIAIiEDCCMgZlTDUulFNCLAJR++NFi1auCOKDbpYC6X5OHr27Gk0nqfJPMe1a9d8A5DMqdWrV/vmH1lVlEuiR5VEBERABEQguwkQKEFGFLqhY8eOnsEUS8nNzfVyf2HJP0rNRuoo9BPPLVy40HtNMQb0E3pKIgIiIAIiIAIiIAIiIAIiIAIiIAIiIAKpSEDOqFRclSwfE9Hmy5Yts379+vnmWqw3+SrDm5OT4z2kOJBbt2552aXr1697hPr27duNTK0333zTM6t69OjhJQPD8yu7pp4TAREQARHIPALvvvuuT2ratGkP7Q8Vq9lTso+DrN0weIJACfQSOvP06dOuNwsLC71cIE4psrUkIiACIiACIiACIiACIiACIiACIiACIpAqBHKCTY2yVBmMxiEC27Ztc8cPDd6J9q5Xr16VUD766COjqfu4cePiXkoPxxROKcojHTlyxMv5bdy40TO3Bg4c6E4zlUuqcqn0ggiIgAikPQEyZj/++GMPSKAXIc6h6uRnP/uZPfvss67L4ik4pdBPPPInHWMkkGLnzp2um0IdpaypeK6Cri0CIiACIiACIiACIiACIiACIiACIvAwAnJGPYyQXk8IAZw8e/bssbVr19rjjz8e1ebdjh073DFEFHjz5s0TMs6KN+H+u3fv9oONSrK5OPr27Ws0nMeZhsNMIgIiIAIikJ4EcPBQOnbVqlXWrl07L80XTS/B9957z9q0aeOlZpMh9EVEr4Y6inK3/fv3dx2FQw39RC/F6oI+kjFu3VMEREAEREAEREAEREAEREAEREAERCAzCcgZlZnrmlazunz5smcaEcX94osvRj32VHBGRQ6W8kj08QgPoua7dOniEfT0/8A5hWMqEWUHo4aoE0VABERABKokQLbRiRMnbO/evda1a1cbNGhQledWfCHZzqiK4zl16pTrpwMHDti5c+esU6dO7pzq0KGDl/XDMUW2r0QEREAEREAEREAEREAEREAEREAEREAE4kFAPaPiQVXXjJoAvS7Wr1/v5e9eeOGFqN+XiifiZBoyZIgfCJlSx44dswULFliDBg281wcbfmz2EaGOs4pNQPpVSURABERABFKLAKXvCJIgs2jw4ME1ckSl1kzujQbnEwdlcCk9i37atWuXbdq0yTO+yPbidTKlCgoKrEmTJnJOpeJCakwiIAIiIAIiIAIiIAIiIAIiIAIikKYE5IxK04VL92HfuXPHo7MPHTrkPS169eqVcU4ZNvc4Ro8ebbdv3/a5Xrx40R9xTrHhxyPR6L1797a2bdta06ZN031pNX4REAERSHsCZOx++umnPo+5c+dmnFOGLN0+ffr4gVBy9tatW+6cIkDi+PHjntGLfiKQomPHjq6jeE4iAiIgAiIgAiIgAiIgAiIgAiIgAiIgArUhIGdUbajpPXUiUFRUZPv377ejR496pHnYu6JOF03xN5MFFVne6ezZs0ZZPzYAib7ftm2b/x5GprPxF0aop/jUNDwREAERyCgCly5dshUrVljLli1t5MiRSetJmEioPXr08Nuhp9DRJ0+etOvXr3sAxenTpz14hCASHFPoJnQUfCQiIAIiIAIiIAIiIAIiIAIiIAIiIAIiEC0BOaOiJaXzYkKAzax33nnHM4Bo6t66deuYXDfdLsKGHsIGICUK6d9Bs/mrV68aG6Fs/LEJyKYf5+CwI8tKIgIiIAIiED8CZEPt27fPxo4d6/2UslHI2A2dU8wfvXTlyhV3TpExRv+sVatWeZZU9+7dyw/eJxEBERABERABERABERABERABERABERCBqgjIGVUVGT0fFwKvv/66l61jo0sl6e4hplwfUeYIjikcduFBdDp9PYjSJ7uKzVF6l9DPQyICIiACIhA7Ahs3brRr167ZjBkzvJ+f5B4B+hxyIKFuoucUDir008qVK+21116zyZMnu57iUSICIiACIiACIiACIiACIiACIiACIiACFQnIGVWRiH6PCwHK0f3kJz+x73znO96jgp4UkgcJ4JiiaTwHgtNpxIgRVlZWVt5s/t/+7d/szJkz9thjj1njxo1tzpw5D15Iz4iACIiACERFgF5JixYt8syfp59+2svySUdVjo5SshxImzZtvN8j+okDhmRO/fmf/7l17tzZddewYcOsa9eulV9Mz4qACIiACIiACIiACIiACIiACIiACGQVgZxgA6Esq2asySaUwO3bt70R+tq1a+3zn/+80TQ9VrJjxw7vuTR16tSs6OlRkdsnn3zifaZ4ZFOQXh9sDLIJSLkkGs/HknfF++t3ERABEUh3ApRFRZfQu2/atGkxnc57773n382UpM02oc/Uzp07nS0/jxs3zrOmJk2aVK6f0FMEYEhEQAREQAREQAREQAREQAREQAREQASyg4CcUdmxzkmZJVHm9N8gi2fevHkxb3ae7c6oyEWlvxR9PA4ePGhE+dOTiqwq+kzl5+d7phWP6umRlH8KuqkIiEAKEjhx4oQtWbLEe/JRmi/Wks3OqIosN2zYYASnrFu3zvVTqKNatmxZng2MjpKIgAiIgAiIgAiIgAiIgAiIgAiIgAhkLgGV6cvctU3azOh7dOHCBXeOUO4o7CORtAFlwY2JvifinINIfzZZ6Te1b98+d0LRn4vHZs2aWatWrbxskiLSs+CDoSmKgAg8QODmzZv+HbllyxZ74okn1IPvAUKxf2LMmDF+0QkTJpTrpz179lhxcXG5fkJH4ZwikAKdJhEBERABERABERABERABERABERABEcgsAsqMyqz1TPpsqPoYbvLRAH7IkCHe1ygecvjwYSPaetasWe5gkTxIAMfg+fPn7dy5c35Q1i9S+vfv71HpXbp0efDNekYEREAEMozAjRs3bNOmTXb16lUvn9e2bdu4zXDZsmX+nfv444/H7R7pfmEyqEP9RBBLpOCUQrf36NFDJWfTfaE1fhEQAREQAREQAREQAREQAREQAREICCgzSh+DmBLYvHmzHTt2zIYOHWqdOnWKmyOKQePsoln6nTt3YjqHTLoY2U9w4kAo4XflyhXv44Wjisypu3fv2uLFi329KJ3Ur18/d1BJREAERCCTCJw9e9bQUTg4yCJt3bp1XKeHE+WDDz6I6z3S/eJk63L07t3bCGZBpxNAQYbvtWvX/Gd6TqKvRo4c6Rm+ffv2Tfdpa/wiIAIiIAIiIAIiIAIiIAIiIAIikJUE5IzKymWPz6Tffvtt70lEGZ7Q+RGfO927KhlXRUVFvkkliY4ATiYOHE9s/LHhhzOKbAGi08lqW758uTv4HnnkEY9IT8RaRjd6nSUCIiACtSNAkMT69evd6TFgwABr1KhR7S5Ug3fhOCEAQBIdgZycHHcQhk5C9BK6iACK0tJSL/0LT/7WoNTsiBEjrGfPntawYcPobqCzREAEREAEREAEREAEREAEREAEREAEkkpAzqik4s+Mm+PEWLp0qW/yxbMsX2bQSp1ZsPEX2ZcDxxP9O6ZMmeKOKpxSa9asMcooUc6PQ1lTqbN+GokIiMDDCRCssG3bNjtw4ICNHz/eHfH16+tPn4eTS/4Z9DrkCPUU5WRxShFAgXORjKnXXnvNM3pDHYWTSiICIiACIiACIiACIiACIiACIiACIpCaBLQjk5rrkhajwmFBCR0cUUQnU0JHm3xpsXSVDpKsNo6wx9fTTz/t5+GM2rlzp/dZefXVV73HyuDBg23gwIGeOZWbm2s4tjgkIiACIpAqBHBc8N1FaT76NuGIkqQvgVA3kXFGqcXhw4f7ZA4dOuTrjGOKv0nQTYMGDfJHSgCim9BTEhEQAREQAREQAREQAREQAREQAREQgeQSkDMqufzT9u6UxyPSfOvWrTZ27FjPipJkJgE28yi9yIHQb4q1xwl59epV3/BjkzfsEUafKqLZJSIgAiKQLAJ8N61evdpLkf7Zn/1Zsoah+yaAQK9evYzjqaee8jJ++/fv92PZsmUeKIPzasyYMe6QokwtQRcq7ZeAhdEtREAEREAEREAEREAEREAEREAERKACATmj9JGoMQFKue3YscOOHj3qfYU6d+5c42voDelLgDJIHNOnT/cG87t377bDhw/bypUr3SFFLxYypnBKUV6JaHYcWhIREAERSASBs2fP+vdRx44dbe7cuYm4pe6RIgRwNpExFWZNbd++3R1Ub7zxhuXl5Rml/po3b+59qVq0aOFOqXbt2imzN0XWT8MQAREQAREQAREQAREQAREQARHIbAJyRmX2+sZ8djQS37hxo/dtwBnBZo4kewmwqUcfFg7k4MGDvvFHjxai0MmQIgqdzwmbfzineFRJv+z9zGjmIhAvAnfu3PGsTb6HRowY4VkxkuwmMHToUAeAjkI38dmglB86CscVpYXJnIrUTzwvEQEREAEREAEREAEREAEREAEREAERiD0BOaNizzRjr3jx4kVbvny592qgNB+OCIkIRBIIyzWyAUiTeUr6US7r3LlzduHCBaO8Y0lJiRUUFFiHDh380OdInyEREIG6EsARtWfPHjtx4oT3tOvevbv6BNUVaoa9HydT6JxiaqdOnbLCwkJ3UJ08edKzvdFRZPOG+ql9+/ae5SsRAREQAREQAREQAREQAREQAREQARGoOwE5o+rOMCuusGbNGi/FNnr0aOvfv39WzFmTrBsBSiKFzqnbt297Sb/wwEG1a9cu7+lCZHpY+q9bt27+u0QEREAEoiVw/fp1W7x4sZcDnTRpkgdMSETgYQQoK4ugp9BJkTqKAIp9+/b585R7RDehp8julYiACIiACIiACIiACIiACIiACIiACNSOgHZ9a8ctq961du1aI+p86tSp6g+VVSsfu8nSR4qDjCiEaHQcVDyy2UeE+ooVK/yRzWTODUv/xW4UupIIiECmESDTcsGCBTZo0CDPhlKmZaatcGLmQynZsOww2VGhfuLxzJkz5X0Rya7q06ePf97atm2bmMHpLiIgAiIgAiIgAiIgAiIgAiIgAiKQIQTk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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each point is a state of the model. $\\psi_{\\rm{a},2}$ and $\\psi_{\\rm{a},3}$ correspond to the 2nd and 3rd variables of the model. These are *spectral* coefficients of the model. Indeed, qgs is a spectral model. It means that all its fields are expanded on spatial modes. Here for instance, there are two fields, the 500hPa streamfunction $\\psi_{\\rm{a}}$ and the 500hPa temperature $\\theta_{\\rm{a}}$. They are expanded on the 10 modes (because we decided to expand up to wavenumber 2 in each direction):\n", + "\n", + "$$\\psi_{\\rm{a}} = \\sum_{i=1}^{10} \\, \\psi_{\\rm{a},i} \\, F_i(x,y)\\qquad\\qquad \\mathrm{(1)}$$ \n", + "$$\\theta_{\\rm{a}} = \\sum_{i=1}^{10} \\, \\theta_{\\rm{a},i} \\, F_i(x,y) \\qquad\\qquad\\,\\, \\mathrm{(2)}$$\n", + "\n", + "where\n", + "\n", + "$$ F_1(x,y) = \\sqrt{2}\\, \\cos(y), \\\\ F_2(x,y) = 2\\, \\cos(n x)\\, \\sin(y), \\\\ F_3(x,y) = 2\\, \\sin(n x)\\, \\sin(y), \\\\ F_4(x,y) = \\sqrt{2}\\, \\cos(2y), \\\\ F_5(x,y) = 2 \\cos(n x) \\sin(2y), \\\\ F_6(x,y) = 2 \\sin(n x) \\sin(2y), \\\\ \\vdots $$\n", + "\n", + "and where $x$ and $y$ are the nondimensional coordinates on the model spatial domain (a $\\beta$-plane): $x \\in [0, 2\\pi/n]$ and $y \\in [0,\\pi]$, with $n$ the aspect ratio of the domain, i.e. the ratio between the zonal and meridional extent of the domain.\n", + "\n", + "Here are the 10 modes (shown for $n=1$) over which the spatial fields $\\psi_{\\rm{a}}$ and $\\theta_{\\rm{a}}$ of the model are decomposed:\n", + "![image.png](attachment:image.png)\n", + "\n", + "Now, when we integrate $\\dot{\\boldsymbol{x}} = \\boldsymbol{f}(\\boldsymbol{x})$, we integrate a differential equation for the time evolution of the spectral coefficients $\\psi_{\\rm{a}, i}$ and $\\theta_{\\rm{a}, i}$. That is, in our case, $\\boldsymbol{x} = (\\psi_{\\rm{a}, 1}, \\ldots, \\psi_{\\rm{a}, 10}, \\theta_{\\rm{a}, 1}, \\ldots, \\theta_{\\rm{a}, 10})$, and solving the model differential equation allow us to determine $\\boldsymbol{x}(t)$. With the equations (1) and (2), it is then easy to reconstruct the time evolution of the spatial fields of the model. That is exactly what the [`Diagnostic`](https://qgs.readthedocs.io/en/latest/files/technical/diagnostics.html) objects in the model do, and the subject of the next section.\n", + "\n", + "> **Info:** See this [page](https://qgs.readthedocs.io/en/latest/files/model/oro_model.html) for more details about the spectral expansion. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Computing and plotting spatial fields of the model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Computation and plotting of the model fields can be done with objects called diagnostics, which take the output of the model integration (i.e. the spectral coefficients) and convert it to spatial fields. A list of currently available diagnostics is included in this section of the [User guide on diagnostics usage](https://qgs.readthedocs.io/en/latest/files/user_guide.html#analyzing-the-model-s-output-with-diagnostics)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this tutorial, we are going to compute the 500hPa geopotential height spatial field. This can be done with the [`MiddleAtmosphericStreamfunctionDiagnostic`](https://qgs.readthedocs.io/en/latest/files/technical/diagnostics.html#qgs.diagnostics.streamfunctions.MiddleAtmosphericStreamfunctionDiagnostic) which compute $\\psi_{\\rm a}(x, y)$." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To compute it, we start by instantiating the object:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "psi = MiddleAtmosphericStreamfunctionDiagnostic(model_parameters, geopotential=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "where the `geopotential=True` statement is just to specify that we want the streamfunction to be expressed as a [geopotential height](https://en.wikipedia.org/wiki/Geopotential_height) anomaly. Note also that we need to pass the `model_parameters` object that we defined above." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we need to pass the model integration data to with the [`set_data`](https://qgs.readthedocs.io/en/latest/files/technical/diagnostics.html#qgs.diagnostics.base.Diagnostic.set_data) method:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "psi.set_data(time, trajectory)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The diagnostic can now compute the fields on the fly by calling" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "psi.diagnostic" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All the spatial fields corresponding to the time evolution of the model variables are returned as an array on specific points $x$ and $y$:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "psi.diagnostic.shape" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here there are 200001 recorded time step of a field represented by a 10 by 10 grid. We can plot a single field at the 100-th recorded time steps with:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(psi.diagnostic[100])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "or use the built-in diagnostic plot function:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "psi.plot(100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As a final remark, please note that the $x$ or $y$ coordinates of the grid points are available in the diagnostics object as:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "psi._X" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "psi._Y" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Showing the resulting fields as an animation\n", + "\n", + "With diagnostics, one can also show the time evolution of fields of the model. Here, we show simultaneously a scatter plot of the variable $\\psi_{{\\rm a}, 2}$ and $\\psi_{{\\rm a}, 3}$, and the geopotential height field at 500 hPa over the orographic height. For more details on the diagnostics and how to use them to produce this kind of animation, please read this section of the [User guide](https://qgs.readthedocs.io/en/latest/files/user_guide.html#analyzing-the-model-s-output-with-diagnostics)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "psi = MiddleAtmosphericStreamfunctionDiagnostic(model_parameters, geopotential=True)\n", + "variable_nondim = VariablesDiagnostic([2, 1], model_parameters, False)\n", + "background = VariablesDiagnostic([2, 1], model_parameters, False)\n", + "background.set_data(time, trajectory)\n", + "\n", + "m = MultiDiagnostic(1,2)\n", + "m.add_diagnostic(variable_nondim,\n", + " diagnostic_kwargs={'show_time': False, 'background': background},\n", + " plot_kwargs={'ms': 0.2})\n", + "m.add_diagnostic(psi,\n", + " diagnostic_kwargs={'style': 'contour', 'contour_labels': False},\n", + " plot_kwargs={'colors': 'k'})\n", + "m.set_data(time, trajectory)\n", + "\n", + "m.animate(figsize=(20,6))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exercise" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A simple exercise for this introduction is to restart this notebook a few times, changing some models parameters to see the effect.\n", + "A full description of all the available parameters is provided [here](https://qgs.readthedocs.io/en/latest/files/technical/configuration.html)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From af6c51d8cf231593344f572c7ce10a450d15f131 Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 7 Oct 2024 10:56:50 +0200 Subject: [PATCH 130/143] Bug fix - ground modes --- qgs/basis/base.py | 3 +++ qgs/params/params.py | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/qgs/basis/base.py b/qgs/basis/base.py index 2807ac6..7999ef7 100644 --- a/qgs/basis/base.py +++ b/qgs/basis/base.py @@ -54,6 +54,9 @@ def __str__(self): def __len__(self): return self.functions.__len__() + def __delitem__(self, key): + self.functions.__delitem__(key) + def append(self, item): self.functions.append(item) diff --git a/qgs/params/params.py b/qgs/params/params.py index ce71ed2..f17e41f 100644 --- a/qgs/params/params.py +++ b/qgs/params/params.py @@ -50,7 +50,8 @@ from qgs.basis.fourier import contiguous_channel_basis, contiguous_basin_basis from qgs.basis.fourier import ChannelFourierBasis, BasinFourierBasis -from sympy import Symbol, simplify +from sympy import simplify, Symbol + # TODO: - store model version in a variable somewhere # - force or warn the user to define the aspect ratio n at parameter object instantiation @@ -1187,7 +1188,7 @@ def set_params(self, dic): if 'ground_params' in self.__dict__.keys(): if self.ground_params is not None: self.ground_params.set_params(dic) - + if 'otemperature_params' in self.__dict__.keys(): if self.gotemperature_params is not None: self.gotemperature_params.set_params(dic) @@ -1427,6 +1428,8 @@ def ground_basis(self, basis): self._oms = None self._gms = None + if basis[0] == 1 or basis[0] == Symbol("1"): + del basis[0] self._ground_basis = basis self._number_of_ground_modes = len(basis) self._number_of_oceanic_modes = 0 @@ -1727,10 +1730,10 @@ def set_ground_channel_fourier_modes(self, nxmax=None, nymax=None, auto=True, mo Parameters ---------- - nxmax: int - Maximum x-wavenumber to fill the spectral block up to. - nymax: int - Maximum :math:`y`-wavenumber to fill the spectral block up to. + nxmax: int, optional + Maximum x-wavenumber to fill the spectral block up to. Default to `None`. + nymax: int, optional + Maximum :math:`y`-wavenumber to fill the spectral block up to. Default to `None`. auto: bool, optional Automatically instantiate the parameters container needed to describe the atmospheric models parameters. Default is `True`. @@ -1739,6 +1742,11 @@ def set_ground_channel_fourier_modes(self, nxmax=None, nymax=None, auto=True, mo `analytic` for inner products computed with formula or `symbolic` using `Sympy`_. Default to `analytic`. + Notes + ----- + If both `nxmax` and `nymax` are `None`, default to the atmospheric basis configuration if available. + + Examples -------- From 75d9a237f3b8b35e868041724c7007350f6dd356 Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 7 Oct 2024 17:18:13 +0200 Subject: [PATCH 131/143] Inclusion of translating final outputs - bug found where EPSILON was included in fortran output --- qgs/functions/symbolic_tendencies.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qgs/functions/symbolic_tendencies.py b/qgs/functions/symbolic_tendencies.py index 18abdda..d75dd70 100644 --- a/qgs/functions/symbolic_tendencies.py +++ b/qgs/functions/symbolic_tendencies.py @@ -23,7 +23,8 @@ } fortran_lang_translation = { - 'conjugate': 'CONJG' + 'conjugate': 'CONJG', + 'epsilon': 'eps' # Remove conflict for EPSILON function in fortran # TODO: may need to add variable for pi } @@ -369,6 +370,7 @@ def equation_as_function(equations, params, language='python', continuation_vari f_output.append('\treturn F') f_output = '\n'.join(f_output) + f_output = translate_equations(f_output, language='python') if language == 'julia': eq_dict = translate_equations(eq_dict, language='julia') @@ -385,6 +387,7 @@ def equation_as_function(equations, params, language='python', continuation_vari f_output.append('end') f_output = '\n'.join(f_output) + f_output = translate_equations(f_output, language='julia') if language == 'fortran': eq_dict = translate_equations(eq_dict, language='fortran') @@ -408,6 +411,7 @@ def equation_as_function(equations, params, language='python', continuation_vari f_output.append('END SUBROUTINE') f_output = '\n'.join(f_output) + f_output = translate_equations(f_output, language='fortran') if language == 'auto': eq_dict = translate_equations(eq_dict, language='fortran') @@ -680,6 +684,7 @@ def _split_equations(eq_dict, f_output, line_len=80, two_dim=False): """Function to split FORTRAN equations to a set length when producing functions""" for n, eq in eq_dict.items(): + print(eq) # split equations to be a maximum of `line_len` # split remainder of equation into chunks of length `line_length` From f093252c9910c0c8b39fbe09b030d28b2943ccba Mon Sep 17 00:00:00 2001 From: ushham Date: Mon, 7 Oct 2024 17:19:17 +0200 Subject: [PATCH 132/143] removal of rogue print statement --- qgs/functions/symbolic_tendencies.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qgs/functions/symbolic_tendencies.py b/qgs/functions/symbolic_tendencies.py index d75dd70..ea2047b 100644 --- a/qgs/functions/symbolic_tendencies.py +++ b/qgs/functions/symbolic_tendencies.py @@ -684,7 +684,6 @@ def _split_equations(eq_dict, f_output, line_len=80, two_dim=False): """Function to split FORTRAN equations to a set length when producing functions""" for n, eq in eq_dict.items(): - print(eq) # split equations to be a maximum of `line_len` # split remainder of equation into chunks of length `line_length` From f420fe70358bf09e2d6837a28d0692ded071de35 Mon Sep 17 00:00:00 2001 From: ushham Date: Tue, 8 Oct 2024 10:27:51 +0200 Subject: [PATCH 133/143] Working function translation - fortran EPSILON bug fix --- qgs/functions/symbolic_tendencies.py | 79 ++++++++++++++++++---------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/qgs/functions/symbolic_tendencies.py b/qgs/functions/symbolic_tendencies.py index ea2047b..5e1c054 100644 --- a/qgs/functions/symbolic_tendencies.py +++ b/qgs/functions/symbolic_tendencies.py @@ -197,7 +197,7 @@ def translate_equations(equations, language='python'): Parameters ---------- - equations: dict(~sympy.core.expr.Expr) + equations: dict(string) Dictionary of the symbolic model equations. language: string Language syntax that the equations are returned in. Options are: @@ -227,11 +227,16 @@ def translate_equations(equations, language='python'): if isinstance(equations, dict): str_eq = dict() for key in equations.keys(): - temp_str = str(equations[key]) - # TODO: This only works for single array, not jacobian + temp_str = equations[key] for k in translator.keys(): temp_str = temp_str.replace(k, translator[k]) str_eq[key] = temp_str + elif isinstance(equations, list): + str_eq = list() + for eq in equations: + for k in translator.keys(): + eq = eq.replace(k, translator[k]) + str_eq.append(eq) else: temp_str = str(equations) for k in translator.keys(): @@ -317,6 +322,27 @@ def format_equations(equations, params, save_loc=None, language='python', print_ return equation_dict +def equations_to_string(equations): + """ + Converts the symbolic equations, held in a dict format, to a dict of strings. + + Parameters + ---------- + equations: dict(~sympy.core.expr.Expr) + Dictionary of the substituted symbolic model equations. + + Returns + ------- + dict(~string) + Dictionary of the substituted symbolic model equations. + """ + + str_eq = dict() + for key in equations.keys(): + str_eq[key] = str(equations[key]) + return str_eq + + def equation_as_function(equations, params, language='python', continuation_variables=None): """Converts the symbolic equations to a function in string format in the language syntax specified, or a lambdified python function. @@ -348,6 +374,7 @@ def equation_as_function(equations, params, language='python', continuation_vari continuation_variables = list() eq_dict = format_equations(equations, params, language=language) + eq_dict = equations_to_string(eq_dict) f_output = list() if language == 'python': @@ -366,15 +393,13 @@ def equation_as_function(equations, params, language='python', continuation_vari f_output.append('\tF = np.empty_like(U)') for n, eq in eq_dict.items(): - f_output.append('\tF['+str(n-1)+'] = ' + str(eq)) + f_output.append('\tF['+str(n-1)+'] = ' + eq) f_output.append('\treturn F') - f_output = '\n'.join(f_output) f_output = translate_equations(f_output, language='python') + f_output = '\n'.join(f_output) if language == 'julia': - eq_dict = translate_equations(eq_dict, language='julia') - f_output.append('function f!(du, U, p, t)') f_output.append('\t# Tendency function of the qgs model') @@ -383,15 +408,13 @@ def equation_as_function(equations, params, language='python', continuation_vari f_output.append('') for n, eq in eq_dict.items(): - f_output.append('\tdu['+str(n)+'] = ' + str(eq)) + f_output.append('\tdu['+str(n)+'] = ' + eq) f_output.append('end') - f_output = '\n'.join(f_output) f_output = translate_equations(f_output, language='julia') + f_output = '\n'.join(f_output) if language == 'fortran': - eq_dict = translate_equations(eq_dict, language='fortran') - f_var = '' for fv in continuation_variables: f_var += ', ' + str(fv.symbol) @@ -410,25 +433,25 @@ def equation_as_function(equations, params, language='python', continuation_vari f_output = _split_equations(eq_dict, f_output) f_output.append('END SUBROUTINE') - f_output = '\n'.join(f_output) f_output = translate_equations(f_output, language='fortran') + f_output = '\n'.join(f_output) if language == 'auto': - eq_dict = translate_equations(eq_dict, language='fortran') - eq_dict = _split_equations(eq_dict, f_output) - f_output = create_auto_file(eq_dict, params, continuation_variables) - + auto_file, auto_config = create_auto_file(eq_dict, params, continuation_variables) + auto_file, auto_config = ( + translate_equations(auto_file, language='fortran'), translate_equations(auto_config, language='fortran')) + f_output = ['\n'.join(auto_file), '\n'.join(auto_config)] + if language == 'mathematica': # TODO: This function needs testing before release - eq_dict = translate_equations(eq_dict, language='mathematica') - f_output.append('F = Array[' + str(len(eq_dict)) + ']') for n, eq in eq_dict.items(): f_output.append('F['+str(n)+'] = ' + str(eq)) # TODO !!!! Killing output as I have not tested the above code !!!! + eq_dict = translate_equations(eq_dict, language='mathematica') f_output = '\n'.join(f_output) f_output = None @@ -466,6 +489,7 @@ def jacobian_as_function(equations, params, language='python', continuation_vari continuation_variables = list() eq_dict = format_equations(equations, params, language=language) + eq_dict = equations_to_string(eq_dict) f_output = list() if language == 'python': @@ -490,8 +514,6 @@ def jacobian_as_function(equations, params, language='python', continuation_vari f_output = '\n'.join(f_output) if language == 'julia': - eq_dict = translate_equations(eq_dict, language='julia') - f_output.append('function jac!(du, U, p, t)') f_output.append('\t# Jacobian function of the qgs model') @@ -503,11 +525,10 @@ def jacobian_as_function(equations, params, language='python', continuation_vari f_output.append('\tdu[' + str(n[0]) + ', ' + str(n[1]) + '] = ' + str(eq)) f_output.append('end') + eq_dict = translate_equations(eq_dict, language='julia') f_output = '\n'.join(f_output) if language == 'fortran': - eq_dict = translate_equations(eq_dict, language='fortran') - f_var = '' for fv in continuation_variables: @@ -527,24 +548,26 @@ def jacobian_as_function(equations, params, language='python', continuation_vari f_output = _split_equations(eq_dict, f_output, two_dim=True) f_output.append('END SUBROUTINE') + eq_dict = translate_equations(eq_dict, language='fortran') f_output = '\n'.join(f_output) if language == 'auto': - eq_dict = translate_equations(eq_dict, language='fortran') - eq_dict = _split_equations(eq_dict, f_output, two_dim=True) - f_output = create_auto_file(eq_dict, params, continuation_variables) + auto_file, auto_config = create_auto_file(eq_dict, params, continuation_variables) + auto_file, auto_config = ( + translate_equations(auto_file, language='fortran'), translate_equations(auto_config, language='fortran')) + f_output = ['\n'.join(auto_file), '\n'.join(auto_config)] if language == 'mathematica': # TODO: This function needs testing before release - eq_dict = translate_equations(eq_dict, language='mathematica') f_output.append('jac = Array[' + str(len(eq_dict)) + ']') for n, eq in eq_dict.items(): - f_output.append('jac[' + str(n[0]) + ', ' + str(n[1]) + '] = ' + str(eq)) + f_output.append('jac[' + str(n[0]) + ', ' + str(n[1]) + '] = ' + eq) # TODO !!!! Killing output as I have not tested the above code !!!! + eq_dict = translate_equations(eq_dict, language='mathematica') f_output = '\n'.join(f_output) f_output = None @@ -677,7 +700,7 @@ def create_auto_file(equations, params, continuation_variables, auto_main_templa else: auto_config.append(ln.replace('\n', '')) - return '\n'.join(auto_file), '\n'.join(auto_config) + return auto_file, auto_config def _split_equations(eq_dict, f_output, line_len=80, two_dim=False): From ffbbb894219fbd1859501a562cf6524573face6d Mon Sep 17 00:00:00 2001 From: ushham Date: Wed, 9 Oct 2024 15:11:42 +0200 Subject: [PATCH 134/143] Bug fix - replacing of strings before eq is split across rows - fortran version --- qgs/functions/symbolic_tendencies.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/qgs/functions/symbolic_tendencies.py b/qgs/functions/symbolic_tendencies.py index 5e1c054..1f23eb3 100644 --- a/qgs/functions/symbolic_tendencies.py +++ b/qgs/functions/symbolic_tendencies.py @@ -197,8 +197,8 @@ def translate_equations(equations, language='python'): Parameters ---------- - equations: dict(string) - Dictionary of the symbolic model equations. + equations: dict(string), list, string + Dictionary, list, or string of the symbolic model equations. language: string Language syntax that the equations are returned in. Options are: - `python` @@ -237,11 +237,12 @@ def translate_equations(equations, language='python'): for k in translator.keys(): eq = eq.replace(k, translator[k]) str_eq.append(eq) - else: - temp_str = str(equations) + elif isinstance(equations, str): + str_eq = equations for k in translator.keys(): - temp_str = temp_str.replace(k, translator[k]) - str_eq = temp_str + str_eq = str_eq.replace(k, translator[k]) + else: + raise warnings.warn("Expected a dict, list, or string input") return str_eq @@ -708,9 +709,11 @@ def _split_equations(eq_dict, f_output, line_len=80, two_dim=False): for n, eq in eq_dict.items(): # split equations to be a maximum of `line_len` - # split remainder of equation into chunks of length `line_length` - eq_chunks = [eq[x: x + line_len] for x in range(0, len(eq), line_len)] + + # First translate the equation to ensure variable names are not split across rows + eq_translated = translate_equations(eq, language='fortran') + eq_chunks = [eq_translated[x: x + line_len] for x in range(0, len(eq_translated), line_len)] if len(eq_chunks) > 1: if two_dim: f_output.append('\tJAC(' + str(n[0]) + ', ' + str(n[1]) + ') =\t ' + eq_chunks[0] + "&") From 157905c4179b530dd2ff13d6750936710689ab9e Mon Sep 17 00:00:00 2001 From: ushham Date: Sat, 21 Dec 2024 10:18:15 +0100 Subject: [PATCH 135/143] Replacing lambda string in python symbolic outputs --- qgs/functions/symbolic_tendencies.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qgs/functions/symbolic_tendencies.py b/qgs/functions/symbolic_tendencies.py index 1f23eb3..4fd9160 100644 --- a/qgs/functions/symbolic_tendencies.py +++ b/qgs/functions/symbolic_tendencies.py @@ -19,7 +19,8 @@ python_lang_translation = { 'sqrt': 'math.sqrt', - 'pi': 'math.pi' + 'pi': 'math.pi', + 'lambda': 'lmda' # Remove conflict for lambda function in python } fortran_lang_translation = { From 3da2ff5d02f785809f4dc4cb7fd1411c8436e6a3 Mon Sep 17 00:00:00 2001 From: ushham Date: Fri, 10 Jan 2025 10:11:22 +0100 Subject: [PATCH 136/143] Bug fix in non-stored symbolic tensor --- qgs/tensors/symbolic_qgtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qgs/tensors/symbolic_qgtensor.py b/qgs/tensors/symbolic_qgtensor.py index bf43a3d..64b0111 100644 --- a/qgs/tensors/symbolic_qgtensor.py +++ b/qgs/tensors/symbolic_qgtensor.py @@ -455,7 +455,7 @@ def _compute_tensor_dicts(self): if gp.orographic_basis == "atmospheric": for jj in range(nvar[0]): for kk in range(nvar[0]): - oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * ap.kd.symbol + oro += a_inv[i, jj] * aips.g(offset + jj, j, offset + kk) * hk_sym_arr[kk] else: for jj in range(nvar[0]): for kk in range(nvar[0]): From e905c630797772d2c740fc29f05300abdf16d1f9 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 11 Mar 2025 14:36:08 +0100 Subject: [PATCH 137/143] Cleaning notebooks --- .../Symbolic Output-Linear-ground test.ipynb | 494 ------------------ notebooks/Symbolic Output-Linear.ipynb | 431 --------------- 2 files changed, 925 deletions(-) delete mode 100644 notebooks/Symbolic Output-Linear-ground test.ipynb delete mode 100644 notebooks/Symbolic Output-Linear.ipynb diff --git a/notebooks/Symbolic Output-Linear-ground test.ipynb b/notebooks/Symbolic Output-Linear-ground test.ipynb deleted file mode 100644 index 4cfac4d..0000000 --- a/notebooks/Symbolic Output-Linear-ground test.ipynb +++ /dev/null @@ -1,494 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "037e82a6", - "metadata": {}, - "source": [ - "# Linear symbolic Land-atmosphere example " - ] - }, - { - "cell_type": "markdown", - "id": "57e1a1bb", - "metadata": {}, - "source": [ - "Testing platform for the symbolic equation version of the qgs model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12f59a3f", - "metadata": {}, - "outputs": [], - "source": [ - "import sys, os\n", - "sys.path.extend([os.path.abspath('../')])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b3814d5", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import sympy as sy\n", - "import sparse as sp\n", - "import math" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7711e102", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.params.params import QgParams\n", - "from qgs.functions.tendencies import create_tendencies" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8500ea89", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, GroundSymbolicInnerProducts\n", - "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT\n", - "from qgs.tensors.symbolic_qgtensor import _symbolic_tensordot, _shift_dict_keys, _add_to_dict" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf632fac", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters = QgParams({'phi0_npi': np.deg2rad(50.)/np.pi, 'n':1.3 }, dynamic_T=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2a8e7c1", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", - "# Mode truncation at the wavenumber 2 in the x and at the \n", - "# wavenumber 4 in the y spatial coordinates for the ocean\n", - "model_parameters.set_ground_channel_fourier_modes(2, 2, mode=\"symbolic\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3237dc76", - "metadata": {}, - "outputs": [], - "source": [ - "# Changing (increasing) the orography depth\n", - "model_parameters.ground_params.set_orography(0.2, 1)\n", - "# Setting the parameters of the heat transfer from the soil\n", - "model_parameters.gotemperature_params.set_params({'gamma': 1.6e7, 'T0': 300})\n", - "model_parameters.atemperature_params.set_params({ 'hlambda':10, 'T0': 290})\n", - "# Setting atmospheric parameters\n", - "model_parameters.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.085, 'kdp': 0.02})\n", - "\n", - "# Setting insolation \n", - "model_parameters.gotemperature_params.set_params({})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "51907767", - "metadata": {}, - "outputs": [], - "source": [ - "C_g = 300\n", - "model_parameters.atemperature_params.set_insolation(0.4*C_g , 0)\n", - "\n", - "model_parameters.gotemperature_params.set_insolation(C_g , 0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6112c83a", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "# Takes ~1 mins\n", - "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=False) # <- Can be turned off once saved" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "805e53eb", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "gnd_ip_stored = GroundSymbolicInnerProducts(model_parameters, stored=True, return_symbolic=True, make_substitution=True) # <- Can be turned off once saved" - ] - }, - { - "cell_type": "markdown", - "id": "d43d341b", - "metadata": {}, - "source": [ - "## Outputting model equations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f67ffc01", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.functions.symbolic_tendencies import create_symbolic_equations, translate_equations, equation_as_function, format_equations" - ] - }, - { - "cell_type": "markdown", - "id": "8ddc3286", - "metadata": {}, - "source": [ - "Calculating the functions and tensor" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8287cf38", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.ground_params.hk[0].symbol" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9479fcb0", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.atemperature_params.eps], language='python')" - ] - }, - { - "cell_type": "markdown", - "id": "0c185e14", - "metadata": {}, - "source": [ - "Convert the dictionary of functions to a lambdified function (not working with numba)\n", - "The input `remain_variables` specifies which variables not to substitute with values." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0fb36604", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "print(funcs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ec572eb", - "metadata": {}, - "outputs": [], - "source": [ - "from numba import njit" - ] - }, - { - "cell_type": "markdown", - "id": "2bd791a9", - "metadata": {}, - "source": [ - "Below function has the **kwargs input removed as I cannot get this to work with numba" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5d98d6b", - "metadata": {}, - "outputs": [], - "source": [ - "@njit\n", - "def f(t, U):\n", - "\t#Tendency function of the qgs model\n", - "\tF = np.empty_like(U)\n", - "\tC_go = 300\n", - "\tC_a = 0.4 * C_go\n", - "\tF[0] = -0.0425*U[0] + 0.0425*U[10] - 0.156054828133898*U[12] + 0.156054828133898*U[2]\n", - "\tF[1] = -0.980418808722262*U[0]*U[2] - 0.980418808722262*U[10]*U[12] + 0.0425*U[11] - 1.56867009395562*U[13]*U[15] - 1.50055762081784*U[14]*U[17] + 1.50055762081784*U[15]*U[16] - 0.0425*U[1] + 0.101317695204044*U[2] - 1.56867009395562*U[3]*U[5] - 1.50055762081784*U[4]*U[7] + 1.50055762081784*U[5]*U[6]\n", - "\tF[2] = 0.980418808722262*U[0]*U[1] - 0.0580129472616723*U[0] + 0.980418808722262*U[10]*U[11] + 0.0580129472616723*U[10] + 0.0425*U[12] + 1.56867009395562*U[13]*U[14] + 1.50055762081784*U[14]*U[16] + 1.50055762081784*U[15]*U[17] - 0.101317695204044*U[1] - 0.0425*U[2] + 1.56867009395562*U[3]*U[4] + 1.50055762081784*U[4]*U[6] + 1.50055762081784*U[5]*U[7]\n", - "\tF[3] = 1.87265793760678*U[11]*U[15] - 1.87265793760678*U[12]*U[14] + 0.0425*U[13] - 0.0624219312535594*U[15] + 3.74531587521356*U[16]*U[19] - 3.74531587521356*U[17]*U[18] + 1.87265793760678*U[1]*U[5] - 1.87265793760678*U[2]*U[4] - 0.0425*U[3] + 0.0624219312535594*U[5] + 3.74531587521356*U[6]*U[9] - 3.74531587521356*U[7]*U[8]\n", - "\tF[4] = -1.02902937637678*U[0]*U[5] - 1.02902937637678*U[10]*U[15] + 1.73752196836555*U[11]*U[17] + 0.574852231579351*U[12]*U[13] - 1.73752196836555*U[12]*U[16] + 0.0425*U[14] - 0.0342706502636204*U[17] + 1.73752196836555*U[1]*U[7] + 0.574852231579351*U[2]*U[3] - 1.73752196836555*U[2]*U[6] - 0.0425*U[4] + 0.0478988752370612*U[5] + 0.0342706502636204*U[7]\n", - "\tF[5] = 1.02902937637678*U[0]*U[4] + 1.02902937637678*U[10]*U[14] - 0.574852231579351*U[11]*U[13] - 1.73752196836555*U[11]*U[16] - 1.73752196836555*U[12]*U[17] + 0.0438818497388818*U[13] + 0.0425*U[15] + 0.0342706502636204*U[16] - 0.574852231579351*U[1]*U[3] - 1.73752196836555*U[1]*U[6] - 1.73752196836555*U[2]*U[7] - 0.0438818497388818*U[3] - 0.0478988752370612*U[4] - 0.0425*U[5] - 0.0342706502636204*U[6]\n", - "\tF[6] = -2.71889339738442*U[0]*U[7] - 2.71889339738442*U[10]*U[17] + 0.753865979381443*U[11]*U[15] + 0.753865979381443*U[12]*U[14] - 4.35022943581506*U[13]*U[19] - 0.0251288659793814*U[15] + 0.0425*U[16] + 0.753865979381443*U[1]*U[5] + 0.753865979381443*U[2]*U[4] - 4.35022943581506*U[3]*U[9] + 0.0251288659793814*U[5] - 0.0425*U[6] + 0.0702434536337315*U[7]\n", - "\tF[7] = 2.71889339738442*U[0]*U[6] + 2.71889339738442*U[10]*U[16] - 0.753865979381443*U[11]*U[14] + 0.753865979381443*U[12]*U[15] + 4.35022943581506*U[13]*U[18] + 0.0251288659793814*U[14] + 0.0425*U[17] - 0.753865979381443*U[1]*U[4] + 0.753865979381443*U[2]*U[5] + 4.35022943581506*U[3]*U[8] - 0.0251288659793814*U[4] - 0.0702434536337315*U[6] - 0.0425*U[7]\n", - "\tF[8] = -2.26482546109569*U[0]*U[9] - 2.26482546109569*U[10]*U[19] - 1.7450294536311*U[13]*U[17] + 0.0425*U[18] - 1.7450294536311*U[3]*U[7] - 0.0425*U[8] + 0.050658847602022*U[9]\n", - "\tF[9] = 2.26482546109569*U[0]*U[8] + 2.26482546109569*U[10]*U[18] + 1.7450294536311*U[13]*U[16] + 0.0425*U[19] + 1.7450294536311*U[3]*U[6] - 0.050658847602022*U[8] - 0.0425*U[9]\n", - "\tF[10] = 4.68670500616653e-6*C_a + 0.00386363636363636*U[0] - 0.023715438957012*U[10] - 1.41868025576271*U[11]*U[2] + 1.41868025576271*U[12]*U[1] + 0.0141868025576271*U[12] - 1.13494420461017*U[14]*U[5] + 1.13494420461017*U[15]*U[4] - 2.83736051152543*U[16]*U[7] + 2.83736051152543*U[17]*U[6] - 2.26988840922034*U[18]*U[9] + 2.26988840922034*U[19]*U[8] + 0.00645434108527132*U[20] - 0.0141868025576271*U[2]\n", - "\tF[11] = -1.43757363347933*U[0]*U[12] + 1.02191932371371*U[10]*U[2] - 0.0315441157231782*U[11] + 0.0214771158470353*U[12] + 1.63507091794193*U[13]*U[5] + 1.21855791962175*U[14]*U[7] - 2.30011781356693*U[15]*U[3] - 1.21855791962175*U[15]*U[6] + 1.85472813238771*U[16]*U[5] - 1.85472813238771*U[17]*U[4] + 0.00900906225374311*U[1] + 0.00559477950653936*U[21]\n", - "\tF[12] = 1.43757363347933*U[0]*U[11] + 0.0122974647859652*U[0] - 1.02191932371371*U[10]*U[1] - 0.0122974647859652*U[10] - 0.0214771158470353*U[11] - 0.0315441157231782*U[12] - 1.63507091794193*U[13]*U[4] + 2.30011781356693*U[14]*U[3] - 1.21855791962175*U[14]*U[6] - 1.21855791962175*U[15]*U[7] + 1.85472813238771*U[16]*U[4] + 1.85472813238771*U[17]*U[5] + 0.00559477950653936*U[22] + 0.00900906225374311*U[2]\n", - "\tF[13] = -1.24843862507119*U[11]*U[5] + 1.24843862507119*U[12]*U[4] - 0.0363121306090808*U[13] - 2.3185288751322*U[14]*U[2] + 2.3185288751322*U[15]*U[1] + 0.017834837501017*U[15] - 2.49687725014237*U[16]*U[9] + 2.49687725014237*U[17]*U[8] - 4.63705775026441*U[18]*U[7] + 4.63705775026441*U[19]*U[6] + 0.00507126799557032*U[23] + 0.0121428571428571*U[3] - 0.017834837501017*U[5]\n", - "\tF[14] = -1.16886956037576*U[0]*U[15] + 0.422511733532696*U[10]*U[5] - 0.612715105162524*U[11]*U[7] - 1.38291034440645*U[12]*U[3] + 0.612715105162524*U[12]*U[6] + 1.79985224341047*U[13]*U[2] - 0.0412871146288803*U[14] + 0.0173705927405276*U[15] - 1.87294455066922*U[16]*U[2] + 1.87294455066922*U[17]*U[1] + 0.0124282982791587*U[17] + 0.00452503199094866*U[24] + 0.015412683237731*U[4] - 0.0124282982791587*U[7]\n", - "\tF[15] = 1.16886956037576*U[0]*U[14] - 0.422511733532696*U[10]*U[4] + 1.38291034440645*U[11]*U[3] + 0.612715105162524*U[11]*U[6] + 0.612715105162524*U[12]*U[7] - 1.79985224341047*U[13]*U[1] - 0.0159138129390846*U[13] - 0.0173705927405276*U[14] - 0.0412871146288803*U[15] - 1.87294455066922*U[16]*U[1] - 0.0124282982791587*U[16] - 1.87294455066922*U[17]*U[2] + 0.00452503199094866*U[25] + 0.0159138129390846*U[3] + 0.015412683237731*U[5] + 0.0124282982791587*U[6]\n", - "\tF[16] = -2.94535914360826*U[0]*U[17] + 0.569389237785845*U[10]*U[7] - 0.768581081081081*U[11]*U[5] - 0.768581081081081*U[12]*U[4] + 0.911022780457352*U[13]*U[9] + 1.42736486486486*U[14]*U[2] + 1.42736486486486*U[15]*U[1] + 0.0109797297297297*U[15] - 0.0460906434981493*U[16] + 0.0306919594705944*U[17] - 4.71257462977322*U[19]*U[3] + 0.00399762116767931*U[26] - 0.0109797297297297*U[5] + 0.0185698198198198*U[6]\n", - "\tF[17] = 2.94535914360826*U[0]*U[16] - 0.569389237785845*U[10]*U[6] + 0.768581081081081*U[11]*U[4] - 0.768581081081081*U[12]*U[5] - 0.911022780457352*U[13]*U[8] - 1.42736486486486*U[14]*U[1] - 0.0109797297297297*U[14] + 1.42736486486486*U[15]*U[2] - 0.0306919594705944*U[16] - 0.0460906434981493*U[17] + 4.71257462977322*U[18]*U[3] + 0.00399762116767931*U[27] + 0.0109797297297297*U[4] + 0.0185698198198198*U[7]\n", - "\tF[18] = -2.37660377951895*U[0]*U[19] + 0.0288656329496225*U[10]*U[9] + 1.50101291338039*U[13]*U[7] - 3.30992591155675*U[17]*U[3] - 0.0513521112007289*U[18] + 0.026256705211838*U[19] + 0.00341993024749443*U[28] + 0.0220279383429672*U[8]\n", - "\tF[19] = 2.37660377951895*U[0]*U[18] - 0.0288656329496225*U[10]*U[8] - 1.50101291338039*U[13]*U[6] + 3.30992591155675*U[16]*U[3] - 0.026256705211838*U[18] - 0.0513521112007289*U[19] + 0.00341993024749443*U[29] + 0.0220279383429672*U[9]\n", - "\tF[20] = 6.44421938347898e-6*C_go + 0.0172043158333333*U[10] - 0.00976477713178294*U[20]\n", - "\tF[21] = 0.0172043158333333*U[11] - 0.00976477713178294*U[21]\n", - "\tF[22] = 0.0172043158333333*U[12] - 0.00976477713178294*U[22]\n", - "\tF[23] = 0.0172043158333333*U[13] - 0.00976477713178294*U[23]\n", - "\tF[24] = 0.0172043158333333*U[14] - 0.00976477713178294*U[24]\n", - "\tF[25] = 0.0172043158333333*U[15] - 0.00976477713178294*U[25]\n", - "\tF[26] = 0.0172043158333333*U[16] - 0.00976477713178294*U[26]\n", - "\tF[27] = 0.0172043158333333*U[17] - 0.00976477713178294*U[27]\n", - "\tF[28] = 0.0172043158333333*U[18] - 0.00976477713178294*U[28]\n", - "\tF[29] = 0.0172043158333333*U[19] - 0.00976477713178294*U[29]\n", - "\treturn F" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7cededb7", - "metadata": {}, - "outputs": [], - "source": [ - "offset = 1 if model_parameters.dynamic_T else 0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b7e03156", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.ndim" - ] - }, - { - "cell_type": "markdown", - "id": "0579478b", - "metadata": {}, - "source": [ - "## Comparing with numerical results" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77155759", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f38b345f", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0f5941", - "metadata": {}, - "outputs": [], - "source": [ - "integrator = RungeKuttaIntegrator()\n", - "integrator.set_func(f)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "415ab585", - "metadata": {}, - "outputs": [], - "source": [ - "integrator_num = RungeKuttaIntegrator()\n", - "integrator_num.set_func(f_num)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e2b5f14a", - "metadata": {}, - "outputs": [], - "source": [ - "# ICs calculated from long transient\n", - "\n", - "ic = np.array([0.05055959, -0.01639403, -0.01440781, -0.01846523, -0.01352099,\n", - " 0.011685 , -0.00201673, -0.02030682, 0.03923588, -0.02229535,\n", - " 0.0586372 , -0.01805569, -0.01264252, -0.0103574 , -0.00618456,\n", - " 0.01159318, -0.00478694, -0.00782509, 0.01066059, -0.01552667,\n", - " 0.30718325, -0.03247899, -0.04512935, -0.00078786, -0.00067468,\n", - " 0.00183836, 0.00068025, 0.00215424, -0.00322845, -0.00186392])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c6d5773", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", - "reference_time, reference_traj = integrator.get_trajectories()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b38989ba", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator_num.integrate(0., 100000., 0.1, ic=ic, write_steps=5)\n", - "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2f892676", - "metadata": {}, - "outputs": [], - "source": [ - "varx = 2\n", - "vary = 1\n", - "plt.figure(figsize=(12, 10))\n", - "\n", - "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", - "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", - "\n", - "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", - "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "68781088", - "metadata": {}, - "outputs": [], - "source": [ - "varx = 2\n", - "vary = 1\n", - "plt.figure(figsize=(12, 10))\n", - "\n", - "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", - "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", - "\n", - "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", - "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6697d107", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "t_s = 100000\n", - "\n", - "plt.plot(reference_time_num[:t_s] * model_parameters.dimensional_time, reference_traj_num[varx, :t_s])\n", - "plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "76ecb077", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "t_s = 1000\n", - "\n", - "vars = [1, 2]\n", - "\n", - "plt.plot(reference_traj_num[vars, :-1].T)\n", - "# plt.plot(reference_time[:t_s] * model_parameters.dimensional_time, reference_traj[varx, :t_s])\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6d76cd4c", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/notebooks/Symbolic Output-Linear.ipynb b/notebooks/Symbolic Output-Linear.ipynb deleted file mode 100644 index 050197c..0000000 --- a/notebooks/Symbolic Output-Linear.ipynb +++ /dev/null @@ -1,431 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "037e82a6", - "metadata": {}, - "source": [ - "# Linear symbolic MAOOAM example " - ] - }, - { - "cell_type": "markdown", - "id": "57e1a1bb", - "metadata": {}, - "source": [ - "Testing platform for the symbolic equation version of the qgs model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12f59a3f", - "metadata": {}, - "outputs": [], - "source": [ - "import sys, os\n", - "sys.path.extend([os.path.abspath('../')])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b3814d5", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import sympy as sy\n", - "import sparse as sp\n", - "import math" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7711e102", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.params.params import QgParams\n", - "from qgs.functions.tendencies import create_tendencies" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf632fac", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters = QgParams(dynamic_T=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2a8e7c1", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", - "# Mode truncation at the wavenumber 2 in the x and at the \n", - "# wavenumber 4 in the y spatial coordinates for the ocean\n", - "model_parameters.set_oceanic_basin_fourier_modes(2, 4, mode=\"symbolic\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8500ea89", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", - "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "69183ec2", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.params.parameter import Parameter, ScalingParameter\n", - "from sympy import Symbol" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2fa987d2", - "metadata": {}, - "outputs": [], - "source": [ - "from sympy.tensor.array import ImmutableDenseNDimArray" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0165d8ea", - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "model_parameters.print_params()" - ] - }, - { - "cell_type": "markdown", - "id": "d43d341b", - "metadata": {}, - "source": [ - "## Outputting model equations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f67ffc01", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.functions.symbolic_tendencies import create_symbolic_equations, create_auto_file, default_auto_c_template" - ] - }, - { - "cell_type": "markdown", - "id": "8ddc3286", - "metadata": {}, - "source": [ - "Calculating the functions and tensor" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9479fcb0", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "%%time\n", - "funcs, = create_symbolic_equations(model_parameters, continuation_variables=\n", - " {model_parameters.atemperature_params.eps},\n", - " language='python')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5fd8283d", - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "print(funcs)" - ] - }, - { - "cell_type": "markdown", - "id": "2bd791a9", - "metadata": {}, - "source": [ - "Below function has the **kwargs input removed as I cannot get this to work with numba" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0632dd0f", - "metadata": {}, - "outputs": [], - "source": [ - "from numba import njit" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d5d98d6b", - "metadata": {}, - "outputs": [], - "source": [ - "@njit\n", - "def f(t, U):\n", - "\t#Tendency function of the qgs model\n", - "\tF = np.empty_like(U)\n", - "\tF[0] = -0.02*U[0] + 0.02*U[10] + 0.0697343654705087*U[21] + 0.1012581197243*U[23]\n", - "\tF[1] = -1.24659182237138*U[0]*U[2] - 1.24659182237138*U[10]*U[12] + 0.02*U[11] - 1.9945469157942*U[13]*U[15] - 2.59615384615385*U[14]*U[17] + 2.59615384615385*U[15]*U[16] - 0.02*U[1] - 0.00408089597671527*U[20] + 0.115315741885204*U[2] - 1.9945469157942*U[3]*U[5] - 2.59615384615385*U[4]*U[7] + 2.59615384615385*U[5]*U[6]\n", - "\tF[2] = 1.24659182237138*U[0]*U[1] + 1.24659182237138*U[10]*U[11] + 0.02*U[12] + 1.9945469157942*U[13]*U[14] + 2.59615384615385*U[14]*U[16] + 2.59615384615385*U[15]*U[17] - 0.115315741885204*U[1] + 0.02*U[24] - 0.02*U[2] + 1.9945469157942*U[3]*U[4] + 2.59615384615385*U[4]*U[6] + 2.59615384615385*U[5]*U[7]\n", - "\tF[3] = 2.16075915877706*U[11]*U[15] - 2.16075915877706*U[12]*U[14] + 0.02*U[13] + 4.32151831755411*U[16]*U[19] - 4.32151831755411*U[17]*U[18] + 2.16075915877706*U[1]*U[5] - 0.00298520400130602*U[20] + 0.0328850072783872*U[22] - 2.16075915877706*U[2]*U[4] - 0.02*U[3] + 4.32151831755411*U[6]*U[9] - 4.32151831755411*U[7]*U[8]\n", - "\tF[4] = -1.21002512891515*U[0]*U[5] - 1.21002512891515*U[10]*U[15] + 2.43*U[11]*U[17] + 0.345721465404329*U[12]*U[13] - 2.43*U[12]*U[16] + 0.02*U[14] + 2.43*U[1]*U[7] - 0.00619643245104446*U[21] + 0.345721465404329*U[2]*U[3] - 2.43*U[2]*U[6] - 0.02*U[4] + 0.059964185780306*U[5]\n", - "\tF[5] = 1.21002512891515*U[0]*U[4] + 1.21002512891515*U[10]*U[14] - 0.345721465404329*U[11]*U[13] - 2.43*U[11]*U[16] - 2.43*U[12]*U[17] + 0.02*U[15] - 0.345721465404329*U[1]*U[3] - 2.43*U[1]*U[6] + 0.02*U[25] - 2.43*U[2]*U[7] - 0.059964185780306*U[4] - 0.02*U[5]\n", - "\tF[6] = -3.24113873816558*U[0]*U[7] - 3.24113873816558*U[10]*U[17] + 0.675*U[11]*U[15] + 0.675*U[12]*U[14] - 5.18582198106493*U[13]*U[19] + 0.02*U[16] + 0.675*U[1]*U[5] - 0.000265258238486493*U[20] + 0.675*U[2]*U[4] - 5.18582198106493*U[3]*U[9] - 0.02*U[6] + 0.0749552322253824*U[7]\n", - "\tF[7] = 3.24113873816558*U[0]*U[6] + 3.24113873816558*U[10]*U[16] - 0.675*U[11]*U[14] + 0.675*U[12]*U[15] + 5.18582198106493*U[13]*U[18] + 0.02*U[17] - 0.675*U[1]*U[4] + 0.675*U[2]*U[5] + 5.18582198106493*U[3]*U[8] - 0.0749552322253824*U[6] - 0.02*U[7]\n", - "\tF[8] = -2.65939588772561*U[0]*U[9] - 2.65939588772561*U[10]*U[19] - 2.65939588772561*U[13]*U[17] + 0.02*U[18] - 0.00059581081260043*U[21] - 2.65939588772561*U[3]*U[7] - 0.02*U[8] + 0.0576578709426019*U[9]\n", - "\tF[9] = 2.6593958877256*U[0]*U[8] + 2.6593958877256*U[10]*U[18] + 2.6593958877256*U[13]*U[16] + 0.02*U[19] + 2.6593958877256*U[3]*U[6] - 0.0576578709426019*U[8] - 0.02*U[9]\n", - "\tF[10] = 0.00181818181818182*U[0] - 0.0326124628611698*U[10] - 1.63693875664928*U[11]*U[2] + 1.63693875664928*U[12]*U[1] - 1.30955100531943*U[14]*U[5] + 1.30955100531943*U[15]*U[4] - 3.27387751329857*U[16]*U[7] + 3.27387751329857*U[17]*U[6] - 2.61910201063885*U[18]*U[9] + 2.61910201063885*U[19]*U[8] - 0.00633948777004624*U[21] - 0.00920528361130002*U[23] + 0.00805846274736615*U[29] + 0.00322338509894646*U[31] + 0.000468575805854495\n", - "\tF[11] = -1.6647358298754*U[0]*U[12] + 1.05320021890077*U[10]*U[2] - 0.0440556295451221*U[11] + 0.0282849932925971*U[12] + 1.68512035024123*U[13]*U[5] + 1.06132075471698*U[14]*U[7] - 2.66357732780065*U[15]*U[3] - 1.06132075471698*U[15]*U[6] + 2.33490566037736*U[16]*U[5] - 2.33490566037736*U[17]*U[4] + 0.00490566037735849*U[1] + 0.00100097448485469*U[20] - 0.00371538559555317*U[28]\n", - "\tF[12] = 1.6647358298754*U[0]*U[11] - 1.05320021890077*U[10]*U[1] - 0.0282849932925971*U[11] - 0.0440556295451221*U[12] - 1.68512035024123*U[13]*U[4] + 2.66357732780065*U[14]*U[3] - 1.06132075471698*U[14]*U[6] - 1.06132075471698*U[15]*U[7] + 2.33490566037736*U[16]*U[4] + 2.33490566037736*U[17]*U[5] - 0.00490566037735849*U[24] + 0.00490566037735849*U[2] + 0.00875417106918239*U[32]\n", - "\tF[13] = -1.44050610585137*U[11]*U[5] + 1.44050610585137*U[12]*U[4] - 0.0470526493909192*U[13] - 2.67522562515255*U[14]*U[2] + 2.67522562515255*U[15]*U[1] - 2.88101221170274*U[16]*U[9] + 2.88101221170274*U[17]*U[8] - 5.35045125030509*U[18]*U[7] + 5.35045125030509*U[19]*U[6] + 0.000852915428944578*U[20] - 0.00939571636525347*U[22] - 0.00316582465075099*U[28] + 0.00569848437135178*U[30] + 0.00571428571428572*U[3]\n", - "\tF[14] = -1.35185957626052*U[0]*U[15] + 0.421071015556554*U[10]*U[5] - 0.449999999999999*U[11]*U[7] - 1.63996079743079*U[12]*U[3] + 0.45*U[12]*U[6] + 1.90590038620335*U[13]*U[2] - 0.054383821013715*U[14] + 0.0230631483770408*U[15] - 2.31923076923077*U[16]*U[2] + 2.31923076923077*U[17]*U[1] + 0.00238324325040172*U[21] - 0.00302946825483566*U[29] + 0.00769230769230769*U[4]\n", - "\tF[15] = 1.35185957626052*U[0]*U[14] - 0.421071015556554*U[10]*U[4] + 1.63996079743079*U[11]*U[3] + 0.45*U[11]*U[6] + 0.45*U[12]*U[7] - 1.90590038620335*U[13]*U[1] - 0.0230631483770408*U[14] - 0.054383821013715*U[15] - 2.31923076923077*U[16]*U[1] - 2.31923076923077*U[17]*U[2] - 0.00769230769230769*U[25] + 0.00713801641025641*U[33] + 0.00769230769230769*U[5]\n", - "\tF[16] = -3.421202001397*U[0]*U[17] + 0.180063263231421*U[10]*U[7] - 0.7875*U[11]*U[5] - 0.7875*U[12]*U[4] + 0.288101221170274*U[13]*U[9] + 1.4625*U[14]*U[2] + 1.4625*U[15]*U[1] - 0.0629368545736434*U[16] + 0.0374776161126912*U[17] - 5.47392320223521*U[19]*U[3] + 0.000132629119243246*U[20] - 0.000492288591410796*U[28] + 0.01*U[6]\n", - "\tF[17] = 3.421202001397*U[0]*U[16] - 0.180063263231421*U[10]*U[6] + 0.7875*U[11]*U[4] - 0.7875*U[12]*U[5] - 0.288101221170275*U[13]*U[8] - 1.4625*U[14]*U[1] + 1.4625*U[15]*U[2] - 0.0374776161126912*U[16] - 0.0629368545736434*U[17] + 5.4739232022352*U[18]*U[3] + 0.01*U[7]\n", - "\tF[18] = -2.75575081119392*U[0]*U[19] - 0.250522801017629*U[10]*U[9] + 1.00209120407052*U[13]*U[7] - 4.00836481628207*U[17]*U[3] - 0.0677711778901247*U[18] + 0.0325892314023402*U[19] + 0.000336762633208938*U[21] - 0.000428077036009388*U[29] + 0.011304347826087*U[8]\n", - "\tF[19] = 2.75575081119392*U[0]*U[18] + 0.250522801017629*U[10]*U[8] - 1.00209120407052*U[13]*U[6] + 4.00836481628207*U[16]*U[3] - 0.0325892314023402*U[18] - 0.0677711778901247*U[19] + 0.011304347826087*U[9]\n", - "\tF[20] = 7.67246397508814e-8*U[11] + 8.50171631003011e-8*U[13] + 4.72151629236194e-8*U[16] - 7.67246397508814e-8*U[1] - 1.73825166772134e-7*U[20] - 0.000847606278214279*U[21]*U[24] - 0.00143958526617346*U[21]*U[26] - 0.0028522623965306*U[22]*U[25] - 0.00374023087846937*U[22]*U[27] - 0.00571797886096935*U[23]*U[26] - 9.13068063386782e-5*U[24] - 8.50171631003011e-8*U[3] - 4.72151629236194e-8*U[6]\n", - "\tF[21] = 4.24355024932212e-8*U[0] - 4.24355024932212e-8*U[10] + 1.4729372656196e-7*U[14] + 6.12741902497755e-8*U[18] - 0.00108790599513578*U[20]*U[24] + 0.00208179542279068*U[20]*U[26] - 5.06696893769175e-7*U[21] - 0.00678262379683416*U[22]*U[24] - 0.0132966288294373*U[23]*U[25] - 9.11498353211769e-5*U[25] - 1.4729372656196e-7*U[4] - 6.12741902497755e-8*U[8]\n", - "\tF[22] = -1.5233133904442e-7*U[13] - 0.00401776948188515*U[20]*U[25] + 0.00715162967775557*U[20]*U[27] + 0.00140621931865981*U[21]*U[24] - 1.05894699522554e-6*U[22] - 0.0199683143249692*U[23]*U[24] - 9.08894129022384e-5*U[26] + 1.5233133904442e-7*U[3]\n", - "\tF[23] = 1.68582727186388e-8*U[0] - 1.68582727186388e-8*U[10] - 0.0103378871816782*U[20]*U[26] - 0.0021609519012024*U[21]*U[25] + 0.00943082095154381*U[22]*U[24] - 1.82681677295638e-6*U[23] - 9.05273115564549e-5*U[27]\n", - "\tF[24] = -1.80603224383758e-7*U[12] + 0.00193551086316502*U[20]*U[21] + 9.12184436809035e-5*U[20] + 0.00537641906434727*U[21]*U[22] + 0.0105377813661206*U[22]*U[23] - 3.61206448767517e-7*U[24] + 1.80603224383758e-7*U[2]\n", - "\tF[25] = -3.46717381704589e-7*U[15] + 0.00686999688804741*U[20]*U[22] + 0.0154574929981067*U[21]*U[23] + 9.10617760748484e-5*U[21] - 6.93434763409178e-7*U[25] + 3.46717381704589e-7*U[5]\n", - "\tF[26] = -0.000642223843335634*U[20]*U[21] + 0.0160555960833909*U[20]*U[23] + 9.08018558801295e-5*U[22] - 1.24461985142565e-6*U[26]\n", - "\tF[27] = -0.00341156103036174*U[20]*U[22] + 9.04404504616234e-5*U[23] - 2.01101385026168e-6*U[27]\n", - "\tF[28] = -0.000960310243846486*U[11] - 0.0008645829811078*U[13] - 0.000192062048769297*U[16] + 1.125*U[21]*U[32] - 0.375000000000001*U[21]*U[34] + 1.5*U[22]*U[33] - 0.75*U[22]*U[35] + 1.875*U[23]*U[34] - 1.125*U[24]*U[29] - 1.5*U[25]*U[30] + 0.375000000000001*U[26]*U[29] - 1.875*U[26]*U[31] + 0.75*U[27]*U[30] - 0.0012202230620155*U[28]\n", - "\tF[29] = 0.0017291659622156*U[10] - 0.000960310243846486*U[14] - 0.000192062048769297*U[18] - 1.125*U[20]*U[32] + 0.375*U[20]*U[34] + 1.875*U[22]*U[32] + 2.25*U[23]*U[33] + 1.125*U[24]*U[28] - 1.875*U[24]*U[30] - 2.25*U[25]*U[31] - 0.375*U[26]*U[28] - 0.0012202230620155*U[29] + 0.000137865173161608\n", - "\tF[30] = 0.00155624936599404*U[13] - 1.5*U[20]*U[33] + 0.75*U[20]*U[35] - 1.875*U[21]*U[32] + 2.625*U[23]*U[32] + 1.875*U[24]*U[29] - 2.625*U[24]*U[31] + 1.5*U[25]*U[28] - 0.75*U[27]*U[28] - 0.0012202230620155*U[30]\n", - "\tF[31] = 0.00069166638488624*U[10] - 1.875*U[20]*U[34] - 2.25*U[21]*U[33] - 2.625*U[22]*U[32] + 2.625*U[24]*U[30] + 2.25*U[25]*U[29] + 1.875*U[26]*U[28] - 0.0012202230620155*U[31] + 5.51460692646433e-5\n", - "\tF[32] = 0.00226267770542636*U[12] + 1.125*U[20]*U[29] - 1.125*U[21]*U[28] + 1.875*U[21]*U[30] - 1.875*U[22]*U[29] + 2.625*U[22]*U[31] - 2.625*U[23]*U[30] - 0.0012202230620155*U[32]\n", - "\tF[33] = 0.00226267770542636*U[15] + 1.5*U[20]*U[30] + 2.25*U[21]*U[31] - 1.5*U[22]*U[28] - 2.25*U[23]*U[29] - 0.0012202230620155*U[33]\n", - "\tF[34] = -0.375*U[20]*U[29] + 1.875*U[20]*U[31] + 0.375*U[21]*U[28] - 1.875*U[23]*U[28] - 0.0012202230620155*U[34]\n", - "\tF[35] = -0.75*U[20]*U[30] + 0.75*U[22]*U[28] - 0.0012202230620155*U[35]\n", - "\treturn F" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0a2d58c7", - "metadata": {}, - "outputs": [], - "source": [ - "offset = 1 if model_parameters.dynamic_T else 0" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "44be2ce1", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.ndim" - ] - }, - { - "cell_type": "markdown", - "id": "0579478b", - "metadata": {}, - "source": [ - "## Comparing with numerical results" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "77155759", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f38b345f", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "f_num, Df, ten_num = create_tendencies(model_parameters, return_qgtensor=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "98294b26", - "metadata": {}, - "outputs": [], - "source": [ - "f(0, np.ones(36)) - f_num(0, np.ones(36))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9a0f5941", - "metadata": {}, - "outputs": [], - "source": [ - "integrator = RungeKuttaIntegrator()\n", - "integrator.set_func(f)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "415ab585", - "metadata": {}, - "outputs": [], - "source": [ - "integrator_num = RungeKuttaIntegrator()\n", - "integrator_num.set_func(f_num)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e2b5f14a", - "metadata": {}, - "outputs": [], - "source": [ - "# ICs calculated from long transient\n", - "\n", - "ic = np.array([ 0.03099396, -0.00496147, 0.00173704, -0.00135697, -0.00772211,\n", - " -0.01966693, 0.0133646 , 0.001414 , 0.01117828, -0.00041981,\n", - " 0.0296539 , -0.00059846, -0.0054299 , 0.00265086, -0.00813694,\n", - " -0.01703874, 0.00775618, -0.00437281, 0.00628166, -0.00235651,\n", - " -0.00471975, 0.00143941, -0.00162457, -0.00087876, 0.00399296,\n", - " -0.00265117, -0.00265747, 0.00141623, -0.0210313 , 0.05039241,\n", - " 0.01445278, 0.00645707, -0.04806996, -0.06529819, -0.00455508,\n", - " 0.04942686])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9c6d5773", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", - "reference_time, reference_traj = integrator.get_trajectories()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b38989ba", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator_num.integrate(0., 1000000., 0.1, ic=ic, write_steps=5)\n", - "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2f892676", - "metadata": {}, - "outputs": [], - "source": [ - "varx = 21\n", - "vary = 29\n", - "plt.figure(figsize=(12, 10))\n", - "\n", - "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", - "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", - "\n", - "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", - "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6697d107", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "\n", - "plt.plot(reference_traj_num[varx, :])\n", - "plt.plot(reference_traj[varx, :])\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "de641ed5", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "venv_amoc", - "language": "python", - "name": "venv_amoc" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 66c9b5871d02e934c5b1bf02e4efa9f0226491e4 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 11 Mar 2025 14:37:18 +0100 Subject: [PATCH 138/143] Added a new notebook to show how to use the symbolic tendencies --- .../symbolic_output_land_atmosphere.ipynb | 508 ++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 notebooks/symbolic_output_land_atmosphere.ipynb diff --git a/notebooks/symbolic_output_land_atmosphere.ipynb b/notebooks/symbolic_output_land_atmosphere.ipynb new file mode 100644 index 0000000..348a492 --- /dev/null +++ b/notebooks/symbolic_output_land_atmosphere.ipynb @@ -0,0 +1,508 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "037e82a6", + "metadata": {}, + "source": [ + "# Output of the symbolic tendencies: Land-Atmosphere model example " + ] + }, + { + "cell_type": "markdown", + "id": "9b0e76cb", + "metadata": {}, + "source": [ + "In this notebook, we show how to create the symbolic tendencies of the model. Symbolic tendencies here means that it is possible to make any parameter of the model appears in the tendencies equations.\n", + "\n", + "This can be done in several languages (Python, Julia, Fortran), but here, we are going to use Python." + ] + }, + { + "cell_type": "markdown", + "id": "b8b7c180", + "metadata": {}, + "source": [ + "More details about the model used in this notebook can be found in the articles:\n", + "* Hamilton, O., Demaeyer, J., Vannitsem, S., & Crucifix, M. (2025). *Using Unstable Periodic Orbits to Understand Blocking Behaviour in a Low Order Land-Atmosphere Model*. Submitted to Chaos. [preprint](https://doi.org/10.48550/arXiv.2503.02808)\n", + "* Xavier, A. K., Demaeyer, J., & Vannitsem, S. (2024). *Variability and predictability of a reduced-order land–atmosphere coupled model.* Earth System Dynamics, **15**(4), 893-912. [doi:10.5194/esd-15-893-2024](https://doi.org/10.5194/esd-15-893-2024)\n", + "\n", + "or in the documentation." + ] + }, + { + "cell_type": "markdown", + "id": "1c3c8250", + "metadata": {}, + "source": [ + "## Modules import" + ] + }, + { + "cell_type": "markdown", + "id": "cfb7a334", + "metadata": {}, + "source": [ + "First, setting the path and loading of some modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12f59a3f", + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "sys.path.extend([os.path.abspath('../')])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a29d8d6", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from mpl_toolkits.mplot3d import Axes3D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a13ae55", + "metadata": {}, + "outputs": [], + "source": [ + "from numba import njit" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2468708a", + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.integrate import solve_ivp" + ] + }, + { + "cell_type": "markdown", + "id": "029a6f39", + "metadata": {}, + "source": [ + "Importing the model's modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7711e102", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.params.params import QgParams" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e0d469c", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.functions.symbolic_tendencies import create_symbolic_equations\n", + "from qgs.functions.tendencies import create_tendencies" + ] + }, + { + "cell_type": "markdown", + "id": "c85c3ef7", + "metadata": {}, + "source": [ + "## Systems definition" + ] + }, + { + "cell_type": "markdown", + "id": "0a2b894b", + "metadata": {}, + "source": [ + "General parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d73ac5f8", + "metadata": {}, + "outputs": [], + "source": [ + "# Time parameters\n", + "dt = 0.1\n" + ] + }, + { + "cell_type": "markdown", + "id": "f5c74bfa", + "metadata": {}, + "source": [ + "Setting some model parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf632fac", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters = QgParams({'phi0_npi': np.deg2rad(50.)/np.pi, 'n':1.3 }, dynamic_T=False)" + ] + }, + { + "cell_type": "markdown", + "id": "19326037", + "metadata": {}, + "source": [ + "and defining the spectral modes used by the model (they must be *symbolic*)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2a8e7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# Mode truncation at the wavenumber 2 in both x and y spatial coordinate for the atmosphere\n", + "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", + "# Same modes for the ground temperature modes\n", + "model_parameters.set_ground_channel_fourier_modes(2, 2, mode=\"symbolic\")" + ] + }, + { + "cell_type": "markdown", + "id": "8bf273c3", + "metadata": {}, + "source": [ + "Completing the model parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3237dc76", + "metadata": {}, + "outputs": [], + "source": [ + "# Changing (increasing) the orography depth\n", + "model_parameters.ground_params.set_orography(0.2, 1)\n", + "# Setting the parameters of the heat transfer from the soil\n", + "model_parameters.gotemperature_params.set_params({'gamma': 1.6e7, 'T0': 300})\n", + "model_parameters.atemperature_params.set_params({ 'hlambda':10, 'T0': 290})\n", + "# Setting atmospheric parameters\n", + "model_parameters.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.085, 'kdp': 0.02})\n", + "\n", + "# Setting insolation \n", + "model_parameters.gotemperature_params.set_params({})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51907767", + "metadata": {}, + "outputs": [], + "source": [ + "C_g = 300\n", + "model_parameters.atemperature_params.set_insolation(0.4*C_g , 0)\n", + "\n", + "model_parameters.gotemperature_params.set_insolation(C_g , 0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "979e2a7c", + "metadata": {}, + "outputs": [], + "source": [ + "# Printing the model's parameters\n", + "model_parameters.print_params()" + ] + }, + { + "cell_type": "markdown", + "id": "d43d341b", + "metadata": {}, + "source": [ + "## Outputting the model equations" + ] + }, + { + "cell_type": "markdown", + "id": "8ddc3286", + "metadata": {}, + "source": [ + "Calculating the tendencies in Python as a function of the parameters $C_{{\\rm o},0}$, $C_{{\\rm a},0}$, $k_d$ and $k'_d$:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9479fcb0", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "%%time\n", + "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.gotemperature_params.C[0], model_parameters.atemperature_params.C[0], model_parameters.atmospheric_params.kd, model_parameters.atmospheric_params.kdp], language='python')" + ] + }, + { + "cell_type": "markdown", + "id": "c61d613a", + "metadata": {}, + "source": [ + "Let's inspect the output:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bdb4d33f", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "print(funcs)" + ] + }, + { + "cell_type": "markdown", + "id": "23ccb10c", + "metadata": {}, + "source": [ + "Note that the tendencies have been already formatted as a [Numba](https://numba.pydata.org/) function, but it is easy to extract the equations for any other kind of accelerator or simply to produce pure Python code." + ] + }, + { + "cell_type": "markdown", + "id": "da0c63c1", + "metadata": {}, + "source": [ + "It is now easy to get the function into operation:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8fea890", + "metadata": {}, + "outputs": [], + "source": [ + "exec(funcs)" + ] + }, + { + "cell_type": "markdown", + "id": "992a6243", + "metadata": {}, + "source": [ + "and" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ae15534", + "metadata": {}, + "outputs": [], + "source": [ + "f(0.,np.zeros(model_parameters.ndim), 300., 120., 0.085, 0.02)" + ] + }, + { + "cell_type": "markdown", + "id": "0579478b", + "metadata": {}, + "source": [ + "## Comparing with numerical results" + ] + }, + { + "cell_type": "markdown", + "id": "b9be8b1d", + "metadata": {}, + "source": [ + "We can check that the symbolic (parameters dependent) equations are the same as the `qgs` numerical ones (with the same values of the parameters):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f38b345f", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "f_num, Df = create_tendencies(model_parameters)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46d33700", + "metadata": {}, + "outputs": [], + "source": [ + "f_num(0., np.zeros(model_parameters.ndim))" + ] + }, + { + "cell_type": "markdown", + "id": "ea417bb2", + "metadata": {}, + "source": [ + "In addition, one can easily compare the obtained attractors:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a0f5941", + "metadata": {}, + "outputs": [], + "source": [ + "# IC calculated from a long transient\n", + "ic = np.array([0.05055959, -0.01639403, -0.01440781, -0.01846523, -0.01352099,\n", + " 0.011685 , -0.00201673, -0.02030682, 0.03923588, -0.02229535,\n", + " 0.0586372 , -0.01805569, -0.01264252, -0.0103574 , -0.00618456,\n", + " 0.01159318, -0.00478694, -0.00782509, 0.01066059, -0.01552667,\n", + " 0.30718325, -0.03247899, -0.04512935, -0.00078786, -0.00067468,\n", + " 0.00183836, 0.00068025, 0.00215424, -0.00322845, -0.00186392])\n", + "\n", + "# Actual integration\n", + "traj = solve_ivp(f, (0., 100000.), ic, t_eval=np.arange(0, 100000., dt), args=(300., 120., 0.085, 0.02))\n", + "traj_num = solve_ivp(f_num, (0., 100000.), ic, t_eval=np.arange(0, 100000., dt))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f892676", + "metadata": {}, + "outputs": [], + "source": [ + "varx = 2\n", + "vary = 1\n", + "plt.figure(figsize=(12, 10))\n", + "\n", + "plt.plot(traj.y[varx], traj.y[vary], marker='o', ms=0.03, ls='', label='Symbolic tendencies')\n", + "plt.plot(traj_num.y[varx], traj_num.y[vary], marker='o', ms=0.03, ls='', label='Fully numerical tendencies')\n", + "\n", + "\n", + "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');\n", + "# plt.legend()" + ] + }, + { + "cell_type": "markdown", + "id": "91781f9f", + "metadata": {}, + "source": [ + "Fully numerical tendencies attractor is in orange while the symbolic tendencies on is in blue" + ] + }, + { + "cell_type": "markdown", + "id": "f84b7484", + "metadata": {}, + "source": [ + "## Varying the parameters" + ] + }, + { + "cell_type": "markdown", + "id": "6367bee4", + "metadata": {}, + "source": [ + "The obvious possibilities given by the symbolic tendencies are to allow users to easily perform sensitivity analysis:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "291b2342", + "metadata": {}, + "outputs": [], + "source": [ + "# let's start with 4 different values of the friction k_d\n", + "kd_list = [0.06, 0.085, 0.095, 0.1, 0.105, 0.12]\n", + "\n", + "# let's compute the attractor for each\n", + "\n", + "attractor_list = list()\n", + "\n", + "for kd in kd_list:\n", + " attractor_list.append(solve_ivp(f, (0., 100000.), ic, t_eval=np.arange(0, 100000., dt), args=(300., 120., kd, 0.02)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e068b639", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "varx = 2\n", + "vary = 1\n", + "plt.figure(figsize=(18, 6))\n", + "\n", + "k=1\n", + "for kd, attractor in zip(kd_list, attractor_list):\n", + " plt.subplot(2, 3, k)\n", + " plt.plot(attractor.y[varx], attractor.y[vary], marker='o', ms=0.03, ls='', label=f'$k_d$ = {kd}')\n", + " plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", + " plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');\n", + "\n", + " plt.legend()\n", + " k+=1\n", + "\n", + "\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 9ff415f16dd852a45b692d0084bf50fb283e4533 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 11 Mar 2025 14:38:06 +0100 Subject: [PATCH 139/143] Cleaning again --- notebooks/Symbolic Output-dynamic_T.ipynb | 429 ---------------------- 1 file changed, 429 deletions(-) delete mode 100644 notebooks/Symbolic Output-dynamic_T.ipynb diff --git a/notebooks/Symbolic Output-dynamic_T.ipynb b/notebooks/Symbolic Output-dynamic_T.ipynb deleted file mode 100644 index a27cc1a..0000000 --- a/notebooks/Symbolic Output-dynamic_T.ipynb +++ /dev/null @@ -1,429 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "12f59a3f", - "metadata": {}, - "outputs": [], - "source": [ - "import sys, os\n", - "sys.path.extend([os.path.abspath('../')])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b3814d5", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import sympy as sy\n", - "import sparse as sp\n", - "import math\n", - "from numba import njit" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7711e102", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.params.params import QgParams\n", - "from qgs.functions.tendencies import create_tendencies\n", - "from qgs.functions.symbolic_tendencies import create_symbolic_equations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8500ea89", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, OceanicSymbolicInnerProducts\n", - "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf632fac", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters = QgParams({'n': 1.5}, dynamic_T=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2a8e7c1", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", - "# Mode truncation at the wavenumber 2 in the x and at the \n", - "# wavenumber 4 in the y spatial coordinates for the ocean\n", - "model_parameters.set_oceanic_basin_fourier_modes(2, 4, mode=\"symbolic\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d2ef57b2", - "metadata": {}, - "outputs": [], - "source": [ - "# Setting MAOOAM parameters according to the publication linked above\n", - "model_parameters.set_params({'kd': 0.0290, 'kdp': 0.0290, 'r': 1.e-7,\n", - " 'h': 136.5, 'd': 1.1e-7})\n", - "model_parameters.atemperature_params.set_params({'eps': 0.7, 'hlambda': 15.06})\n", - "model_parameters.gotemperature_params.set_params({'gamma': 5.6e8})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8d9733db", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.atemperature_params.set_insolation(103., 0)\n", - "model_parameters.atemperature_params.set_insolation(103., 1)\n", - "model_parameters.gotemperature_params.set_insolation(310., 0)\n", - "model_parameters.gotemperature_params.set_insolation(310., 1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5c0fbea9", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.print_params()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f3b012e9", - "metadata": {}, - "outputs": [], - "source": [ - "f_num, Df = create_tendencies(model_parameters)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8552c7a9", - "metadata": {}, - "outputs": [], - "source": [ - "f_sym, jac_sym = create_symbolic_equations(model_parameters, language='python', continuation_variables=[model_parameters.atemperature_params.eps, model_parameters.atmospheric_params.kd], return_jacobian=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0a5bff28", - "metadata": {}, - "outputs": [], - "source": [ - "print(f_sym)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ded58816", - "metadata": {}, - "outputs": [], - "source": [ - "print(jac_sym)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "469ff2a6", - "metadata": {}, - "outputs": [], - "source": [ - "exec(f_sym)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cd4adc73", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.integrators.integrator import RungeKuttaIntegrator, RungeKuttaTglsIntegrator\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "59922db7", - "metadata": {}, - "outputs": [], - "source": [ - "eps = model_parameters.atemperature_params.eps\n", - "kd = model_parameters.atmospheric_params.kd" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "edc906e8", - "metadata": {}, - "outputs": [], - "source": [ - "@njit\n", - "def f_fix(t, U):\n", - " return f(t, U, eps, kd)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "060ee686", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "integrator = RungeKuttaIntegrator()\n", - "integrator.set_func(f_fix)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d8935949", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "integrator_num = RungeKuttaIntegrator()\n", - "integrator_num.set_func(f_num)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "919f59cc", - "metadata": {}, - "outputs": [], - "source": [ - "ic = np.array([ 3.84101549e-02, -8.29674554e-03, 3.04587364e-02, 2.80766373e-02,\n", - " -9.14885177e-03, -9.17520676e-04, -1.76115081e-02, 1.32010146e-02,\n", - " 1.62515224e-02, 1.08600254e-03, 1.53918671e+00, 4.13205067e-02,\n", - " -9.25169842e-04, 4.01449139e-03, 6.97326597e-03, -9.93383832e-03,\n", - " 8.88594931e-03, -6.07097456e-03, 4.34490969e-03, 4.19834122e-03,\n", - " -2.91974161e-03, 1.03085300e-05, 5.98444985e-04, -2.57753313e-05,\n", - " 5.22115566e-06, -3.01445438e-05, 3.26249104e-04, -1.92171554e-05,\n", - " 1.38469482e-05, 3.17552667e+00, 2.46854576e-03, 1.44249578e-01,\n", - " -5.94828283e-03, 2.34242352e-02, -3.08095487e-03, 9.15501463e-02,\n", - " 1.17932987e-03, -4.34659450e-05])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "76c9285a", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator.integrate(0., 1000000., 0.1, ic=ic, write_steps=10)\n", - "reference_time, reference_traj = integrator.get_trajectories()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b1d3ca4e", - "metadata": {}, - "outputs": [], - "source": [ - "%%time\n", - "integrator_num.integrate(0., 1000000., 0.1, ic=ic, write_steps=10)\n", - "reference_time_num, reference_traj_num = integrator_num.get_trajectories()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fb7b11cf", - "metadata": {}, - "outputs": [], - "source": [ - "varx = 22\n", - "vary = 31\n", - "plt.figure(figsize=(10, 8))\n", - "\n", - "plt.plot(reference_traj[varx], reference_traj[vary], marker='o', ms=0.07, ls='')\n", - "plt.plot(reference_traj_num[varx], reference_traj_num[vary], marker='o', ms=0.07, ls='')\n", - "\n", - "plt.xlabel('$'+model_parameters.latex_var_string[varx]+'$')\n", - "plt.ylabel('$'+model_parameters.latex_var_string[vary]+'$');" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5e3b5782", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "\n", - "plt.plot(reference_traj[varx, :])\n", - "plt.plot(reference_traj_num[varx, :])\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8078daf2", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "\n", - "plt.plot(reference_traj_num[vary, :])\n", - "plt.plot(reference_traj[vary, :])\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6ed354fa", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "\n", - "plt.plot(reference_traj_num[varx, ::1000])\n", - "plt.plot(reference_traj[varx, ::1000])\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "457b65e1", - "metadata": {}, - "outputs": [], - "source": [ - "tendencies_sym = np.empty((38, reference_time[::1000].shape[0]))\n", - "for n, x in enumerate(reference_time[::1000]):\n", - " x = reference_traj_num[:, n]\n", - " tendencies_sym[:, n] = f_fix(0, x)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7ced08cb", - "metadata": {}, - "outputs": [], - "source": [ - "tendencies_sym.shape" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bd90544e", - "metadata": {}, - "outputs": [], - "source": [ - "tendencies_num = np.empty_like(tendencies_sym)\n", - "for n, x in enumerate(reference_time[::1000]):\n", - " x = reference_traj_num[:, n]\n", - " tendencies_num[:, n] = f_num(0, x)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e8ed76ce", - "metadata": {}, - "outputs": [], - "source": [ - "tendencies_err = tendencies_sym - tendencies_num" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4c2303e5", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "varx = 10\n", - "plt.plot(tendencies_err[:, :].T)\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0cdf273c", - "metadata": {}, - "outputs": [], - "source": [ - "plt.figure(figsize=(12, 8))\n", - "varx = [1, 12]\n", - "plt.plot(tendencies_err[varx, :].T)\n", - "\n", - "plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4adeeca5", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.10" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From b7a395b42c9ba5fb6ab22f22ee17fdbe7934b06a Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 11 Mar 2025 17:36:41 +0100 Subject: [PATCH 140/143] Added a notebook for the AUTO symbolic tendencies --- .../Symbolic Output-Linear-ground-AUTO.ipynb | 2 +- ...symbolic_output_land_atmosphere-AUTO.ipynb | 889 ++++++++++++++++++ .../symbolic_output_land_atmosphere.ipynb | 5 +- 3 files changed, 892 insertions(+), 4 deletions(-) create mode 100644 notebooks/symbolic_output_land_atmosphere-AUTO.ipynb diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb index 5c0cc53..0bf2859 100644 --- a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb +++ b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb @@ -717,7 +717,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.8.16" } }, "nbformat": 4, diff --git a/notebooks/symbolic_output_land_atmosphere-AUTO.ipynb b/notebooks/symbolic_output_land_atmosphere-AUTO.ipynb new file mode 100644 index 0000000..06495d6 --- /dev/null +++ b/notebooks/symbolic_output_land_atmosphere-AUTO.ipynb @@ -0,0 +1,889 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "037e82a6", + "metadata": {}, + "source": [ + "# Output of the symbolic tendencies: AUTO example " + ] + }, + { + "cell_type": "markdown", + "id": "9b0e76cb", + "metadata": {}, + "source": [ + "In this notebook, we show how to create the symbolic tendencies of the model and use them to perform continuation of solutions as parameters are varied, using the [AUTO-07p](https://github.com/auto-07p/auto-07p) continuation software. Symbolic tendencies here means that it is possible to make any parameter of the model appears in the tendencies equations.\n", + "\n", + "The present notebook will create the Fortran tendencies equations code and insert it into a given AUTO template.\n", + "This can then be used inside the notebook directly." + ] + }, + { + "cell_type": "markdown", + "id": "b8b7c180", + "metadata": {}, + "source": [ + "More details about the model used in this notebook can be found in the articles:\n", + "* Hamilton, O., Demaeyer, J., Vannitsem, S., & Crucifix, M. (2025). *Using Unstable Periodic Orbits to Understand Blocking Behaviour in a Low Order Land-Atmosphere Model*. Submitted to Chaos. [preprint](https://doi.org/10.48550/arXiv.2503.02808)\n", + "* Xavier, A. K., Demaeyer, J., & Vannitsem, S. (2024). *Variability and predictability of a reduced-order land–atmosphere coupled model.* Earth System Dynamics, **15**(4), 893-912. [doi:10.5194/esd-15-893-2024](https://doi.org/10.5194/esd-15-893-2024)\n", + "\n", + "or in the documentation. In particular, Hamilton et. al. (2025) used the symbolic tendencies here provided to compute periodic orbits, along with an automatic layer for AUTO called [auto-AUTO](https://github.com/Climdyn/auto-AUTO)." + ] + }, + { + "cell_type": "markdown", + "id": "98188e40", + "metadata": {}, + "source": [ + "> **To run this notebook, you need AUTO properly installed and configured !**\n", + ">\n", + "> **In general, it means that typing** `auto` **in a terminal starts the AUTO Python interface.**" + ] + }, + { + "cell_type": "markdown", + "id": "1c3c8250", + "metadata": {}, + "source": [ + "## Modules import" + ] + }, + { + "cell_type": "markdown", + "id": "cfb7a334", + "metadata": {}, + "source": [ + "First, setting the path and loading of some modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12f59a3f", + "metadata": {}, + "outputs": [], + "source": [ + "import sys, os\n", + "sys.path.extend([os.path.abspath('../')])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d1499c4", + "metadata": {}, + "outputs": [], + "source": [ + "import glob" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4a29d8d6", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "markdown", + "id": "029a6f39", + "metadata": {}, + "source": [ + "Importing the model's modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7711e102", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.params.params import QgParams" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e0d469c", + "metadata": {}, + "outputs": [], + "source": [ + "from qgs.functions.symbolic_tendencies import create_symbolic_equations" + ] + }, + { + "cell_type": "markdown", + "id": "c85c3ef7", + "metadata": {}, + "source": [ + "## Systems definition" + ] + }, + { + "cell_type": "markdown", + "id": "f5c74bfa", + "metadata": {}, + "source": [ + "Setting some model parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bf632fac", + "metadata": {}, + "outputs": [], + "source": [ + "model_parameters = QgParams({'phi0_npi': np.deg2rad(50.)/np.pi, 'n':1.3 }, dynamic_T=False)" + ] + }, + { + "cell_type": "markdown", + "id": "19326037", + "metadata": {}, + "source": [ + "and defining the spectral modes used by the model (they must be *symbolic*)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2a8e7c1", + "metadata": {}, + "outputs": [], + "source": [ + "# Mode truncation at the wavenumber 2 in both x and y spatial coordinate for the atmosphere\n", + "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", + "# Same modes for the ground temperature modes\n", + "model_parameters.set_ground_channel_fourier_modes(2, 2, mode=\"symbolic\")" + ] + }, + { + "cell_type": "markdown", + "id": "8bf273c3", + "metadata": {}, + "source": [ + "Completing the model parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3237dc76", + "metadata": {}, + "outputs": [], + "source": [ + "# Changing (increasing) the orography depth\n", + "model_parameters.ground_params.set_orography(0.2, 1)\n", + "# Setting the parameters of the heat transfer from the soil\n", + "model_parameters.gotemperature_params.set_params({'gamma': 1.6e7, 'T0': 300})\n", + "model_parameters.atemperature_params.set_params({ 'hlambda':10, 'T0': 290})\n", + "# Setting atmospheric parameters\n", + "model_parameters.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.085, 'kdp': 0.02})\n", + "\n", + "# Setting insolation \n", + "model_parameters.gotemperature_params.set_params({})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "51907767", + "metadata": {}, + "outputs": [], + "source": [ + "C_g = 300\n", + "model_parameters.atemperature_params.set_insolation(0.4*C_g , 0)\n", + "\n", + "model_parameters.gotemperature_params.set_insolation(C_g , 0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "979e2a7c", + "metadata": {}, + "outputs": [], + "source": [ + "# Printing the model's parameters\n", + "model_parameters.print_params()" + ] + }, + { + "cell_type": "markdown", + "id": "da46eccd", + "metadata": {}, + "source": [ + "## Creating AUTO files" + ] + }, + { + "cell_type": "markdown", + "id": "8ddc3286", + "metadata": {}, + "source": [ + "Calculating the tendencies in Fortran for AUTO as a function of the parameters $C_{{\\rm o},0}$, $C_{{\\rm a},0}$, $k_d$ and $k'_d$:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af1b4747", + "metadata": {}, + "outputs": [], + "source": [ + "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.gotemperature_params.C[0], model_parameters.atemperature_params.C[0], model_parameters.atmospheric_params.kd, model_parameters.atmospheric_params.kdp], language='auto')" + ] + }, + { + "cell_type": "markdown", + "id": "b21db3b5", + "metadata": {}, + "source": [ + "Let's inspect the output. First the AUTO `.f90` file:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2bdd3728", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "print(funcs[0])" + ] + }, + { + "cell_type": "markdown", + "id": "5cb3a9aa", + "metadata": {}, + "source": [ + "and then AUTO `c.` configuration file:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73c0eb0a", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "print(funcs[1])" + ] + }, + { + "cell_type": "markdown", + "id": "bfe60c32", + "metadata": {}, + "source": [ + "We can now use both to write the AUTO files.\n", + "First we will modify the tendencies to force $C_{{\\rm a}, 1} = 0.4 C_{{\\rm g}, 1}$ , which is a standard assumption for these models, and reduces the number of parameters to define :" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "87f86011", + "metadata": {}, + "outputs": [], + "source": [ + "# splitting all the lines of the .f90 file\n", + "auto_eq_lines = funcs[0].split('\\n')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec60bcec", + "metadata": {}, + "outputs": [], + "source": [ + "# forcing the change\n", + "for i, line in enumerate(auto_eq_lines):\n", + " if 'C_a1 = PAR(2)' in line:\n", + " auto_eq_lines[i] = '\\tC_a1 = 0.4*C_go1'\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "626c1862", + "metadata": {}, + "outputs": [], + "source": [ + "# gathering all the lines again in a single string\n", + "auto_eq = '\\n'.join(auto_eq_lines)" + ] + }, + { + "cell_type": "markdown", + "id": "b0d3fb95", + "metadata": {}, + "source": [ + "Taking care of the config file, changing some default settings:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "78c7ceba", + "metadata": {}, + "outputs": [], + "source": [ + "# splitting all the lines of the c. file\n", + "auto_config_lines = funcs[1].split('\\n')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "065e83b3", + "metadata": {}, + "outputs": [], + "source": [ + "# introducing some user defined points for AUTO\n", + "for i, line in enumerate(auto_config_lines):\n", + " if 'UZR' in line:\n", + " auto_config_lines[i] = \"UZR = {'C_go1': \" + str(list(np.arange(50.,375.,50.)))+\"}\"\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cc7b79fe", + "metadata": {}, + "outputs": [], + "source": [ + "# imposing that C_go1 is between 0. and 400. as stopping condition for AUTO\n", + "for i, line in enumerate(auto_config_lines):\n", + " if 'UZSTOP' in line:\n", + " auto_config_lines[i] = \"UZSTOP = {'C_go1': [0.,400.]}\"\n", + " break" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7760092", + "metadata": {}, + "outputs": [], + "source": [ + "# gathering all the lines again in a single string\n", + "auto_config = '\\n'.join(auto_config_lines)" + ] + }, + { + "cell_type": "markdown", + "id": "5289260a", + "metadata": {}, + "source": [ + "and writing to files:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "80f05363", + "metadata": {}, + "outputs": [], + "source": [ + "model_name = 'qgs_land-atmosphere_auto'\n", + "with open(f'{model_name}.f90', 'w') as ff:\n", + " ff.write(auto_eq)\n", + " \n", + "with open(f'c.{model_name}', 'w') as ff:\n", + " ff.write(auto_config)" + ] + }, + { + "cell_type": "markdown", + "id": "3f219527", + "metadata": {}, + "source": [ + "## Defining some plotting functions" + ] + }, + { + "cell_type": "markdown", + "id": "5801aaa0", + "metadata": {}, + "source": [ + "to help us investigate the results later:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29fe26ab", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_branches(filename, variables=(0,1), ax=None, figsize=(10, 8), markersize=12., plot_kwargs=None, marker_kwargs=None, branch_indices='all', excluded_labels=('UZ', 'EP', 'No Label'), variables_name=None):\n", + " \n", + " if ax is None:\n", + " fig = plt.figure(figsize=figsize)\n", + " ax = fig.gca()\n", + " \n", + " if plot_kwargs is None:\n", + " plot_kwargs = dict()\n", + " \n", + " if marker_kwargs is None:\n", + " marker_kwargs = dict()\n", + " \n", + " pb_obj = parseB.parseB()\n", + " fb = open(filename, 'r')\n", + " pb_obj.read(fb)\n", + " \n", + " keys = list(pb_obj.branches[0].keys())\n", + " \n", + " if variables[0] in keys:\n", + " var1 = variables[0]\n", + " else:\n", + " try:\n", + " var1 = keys[variables[0]]\n", + " except:\n", + " var1 = keys[0]\n", + "\n", + " if variables[1] in keys:\n", + " var2 = variables[1]\n", + " else:\n", + " try:\n", + " var2 = keys[variables[1]]\n", + " except:\n", + " var2 = keys[1]\n", + "\n", + " if branch_indices == 'all':\n", + " branch_indices = range(len(pb_obj.branches))\n", + "\n", + " branch_num = list()\n", + " for i in branch_indices:\n", + " branch_dict = pb_obj.branches[i].todict()\n", + " branch_num.append(pb_obj.branches[i]['BR'])\n", + "\n", + " labels = list()\n", + " for j, coords in enumerate(zip(branch_dict[var1], branch_dict[var2])):\n", + " lab = pb_obj.branches[i].labels[j]\n", + " if not lab:\n", + " pass\n", + " else:\n", + " labels.append((coords, list(lab.keys())[0]))\n", + "\n", + " ax.plot(branch_dict[var1], branch_dict[var2], **plot_kwargs)\n", + " if excluded_labels != 'all':\n", + " for label in labels:\n", + " coords = label[0]\n", + " lab = label[1]\n", + " if lab not in excluded_labels:\n", + " ax.text(coords[0], coords[1], r'${\\bf '+ lab + r'}$', fontdict={'family':'sans-serif','size':markersize},va='center', ha='center', **marker_kwargs, clip_on=True)\n", + " \n", + " fb.close()\n", + " if variables_name is None:\n", + " ax.set_xlabel(var1)\n", + " ax.set_ylabel(var2)\n", + " else:\n", + " if isinstance(variables_name, dict):\n", + " ax.set_xlabel(variables_name[var1])\n", + " ax.set_ylabel(variables_name[var2])\n", + " else:\n", + " ax.set_xlabel(variables_name[0])\n", + " ax.set_ylabel(variables_name[1])\n", + " return ax, branch_num" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1b0d5cb", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_branches3d(filename, variables=(0,1,3), ax=None, figsize=(10, 8), markersize=12., plot_kwargs=None, marker_kwargs=None, branch_indices='all', excluded_labels=('UZ', 'EP', 'No Label'), variables_name=None):\n", + " \n", + " if ax is None:\n", + " fig = plt.figure(figsize=figsize)\n", + " ax = plt.subplot(projection='3d')\n", + " \n", + " if plot_kwargs is None:\n", + " plot_kwargs = dict()\n", + " \n", + " if marker_kwargs is None:\n", + " marker_kwargs = dict()\n", + " \n", + " pb_obj = parseB.parseB()\n", + " fb = open(filename, 'r')\n", + " pb_obj.read(fb)\n", + " \n", + " keys = list(pb_obj.branches[0].keys())\n", + " \n", + " if variables[0] in keys:\n", + " var1 = variables[0]\n", + " else:\n", + " try:\n", + " var1 = keys[variables[0]]\n", + " except:\n", + " var1 = keys[0]\n", + "\n", + " if variables[1] in keys:\n", + " var2 = variables[1]\n", + " else:\n", + " try:\n", + " var2 = keys[variables[1]]\n", + " except:\n", + " var2 = keys[1]\n", + " \n", + " if variables[2] in keys:\n", + " var3 = variables[2]\n", + " else:\n", + " try:\n", + " var3 = keys[variables[2]]\n", + " except:\n", + " var3 = keys[2]\n", + "\n", + "\n", + " if branch_indices == 'all':\n", + " branch_indices = range(len(pb_obj.branches))\n", + "\n", + " branch_num = list()\n", + " for i in branch_indices:\n", + " branch_dict = pb_obj.branches[i].todict()\n", + " branch_num.append(pb_obj.branches[i]['BR'])\n", + "\n", + " labels = list()\n", + " for j, coords in enumerate(zip(branch_dict[var1], branch_dict[var2], branch_dict[var3])):\n", + " lab = pb_obj.branches[i].labels[j]\n", + " if not lab:\n", + " pass\n", + " else:\n", + " labels.append((coords, list(lab.keys())[0]))\n", + "\n", + " ax.plot(branch_dict[var1], branch_dict[var2], branch_dict[var3], **plot_kwargs)\n", + " if excluded_labels != 'all':\n", + " for label in labels:\n", + " coords = label[0]\n", + " lab = label[1]\n", + " if lab not in excluded_labels:\n", + " ax.text(coords[0], coords[1], coords[2], r'${\\bf '+ lab + r'}$', fontdict={'family':'sans-serif','size':markersize},va='center', ha='center', **marker_kwargs, clip_on=True)\n", + " \n", + " fb.close()\n", + " if variables_name is None:\n", + " ax.set_xlabel(var1)\n", + " ax.set_ylabel(var2)\n", + " ax.set_zlabel(var3)\n", + " else:\n", + " if isinstance(variables_name, dict):\n", + " ax.set_xlabel(variables_name[var1])\n", + " ax.set_ylabel(variables_name[var2])\n", + " ax.set_zlabel(variables_name[var3])\n", + " else:\n", + " ax.set_xlabel(variables_name[0])\n", + " ax.set_ylabel(variables_name[1])\n", + " ax.set_zlabel(variables_name[2])\n", + " return ax, branch_num" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ff2a41a0", + "metadata": {}, + "outputs": [], + "source": [ + "def plot_branch_vs_others(branch_num, figsize=(10, 16), excluded_labels=('UZ', 'EP', 'No Label')):\n", + " \n", + " fig = plt.figure(figsize=figsize)\n", + " ax = plt.subplot(2,1,1)\n", + " ax3 = plt.subplot(2,1,2, projection='3d')\n", + " \n", + " \n", + " \n", + " fp = glob.glob('./b.fp*')\n", + " fp = [item for item in fp if '~' not in os.path.basename(item)]\n", + " fp = [item for item in fp if '_' not in os.path.basename(item)]\n", + " \n", + " for i in range(len(fp)-1,-1,-1):\n", + " \n", + " try:\n", + " num = int(fp[i][-2:])\n", + " except:\n", + " num = int(fp[i][-1])\n", + " \n", + " if num == branch_num:\n", + " plot_branches(fp[i], ax=ax, plot_kwargs={'color': 'tab:blue', 'zorder': 10.}, variables=(0, 1), variables_name=(r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=excluded_labels)\n", + " plot_branches3d(fp[i], ax=ax3, plot_kwargs={'color': 'tab:blue', 'zorder': 10.}, variables=(3, 0, 1), variables_name=(r'$\\psi_{{\\rm a}, 2}$', r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=excluded_labels)\n", + " else:\n", + " plot_branches(fp[i], ax=ax, plot_kwargs={'color': 'tab:orange'}, variables=(0, 1), variables_name=(r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=\"all\")\n", + " plot_branches3d(fp[i], ax=ax3, plot_kwargs={'color': 'tab:orange'}, variables=(3, 0, 1), variables_name=(r'$\\psi_{{\\rm a}, 2}$', r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=\"all\")\n", + "\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "2d4e97e3", + "metadata": {}, + "source": [ + "## AUTO analysis" + ] + }, + { + "cell_type": "markdown", + "id": "613647e4", + "metadata": {}, + "source": [ + "Initializing AUTO" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0b291604", + "metadata": {}, + "outputs": [], + "source": [ + "# Finding where AUTO is installed\n", + "auto_directory = os.environ['AUTO_DIR']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a70a505f", + "metadata": {}, + "outputs": [], + "source": [ + "# Adding it to the path\n", + "sys.path.append(auto_directory + '/python/auto')\n", + "sys.path.append(auto_directory + '/python')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1ddf964", + "metadata": {}, + "outputs": [], + "source": [ + "# Loading the needed AUTO Python interfaces\n", + "import AUTOCommands as ac\n", + "import AUTOclui as acl\n", + "import interactiveBindings as ib\n", + "import runAUTO as ra\n", + "import parseB, parseC, parseD, parseS, parseBandS" + ] + }, + { + "cell_type": "markdown", + "id": "a30354af", + "metadata": {}, + "source": [ + "Loading the model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d4f451e7", + "metadata": {}, + "outputs": [], + "source": [ + "print(\"Loading model \"+model_name)" + ] + }, + { + "cell_type": "markdown", + "id": "539f2d00", + "metadata": {}, + "source": [ + "Starting a runner" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1240ace5", + "metadata": {}, + "outputs": [], + "source": [ + "runner = ra.runAUTO()\n", + "ac.load(model_name, runner=runner)" + ] + }, + { + "cell_type": "markdown", + "id": "672ef822", + "metadata": {}, + "source": [ + "Finding the first branch of fixed point" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fe0c0600", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "U_dic = {i+1: 0. for i in range(model_parameters.ndim)}\n", + "x = ac.run(model_name, U=U_dic, ICP=['C_go1'], PAR={3: model_parameters.atmospheric_params.kd, 4: model_parameters.atmospheric_params.kdp}, runner=runner)\n", + "ac.save(x,'fp1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "375f5335", + "metadata": {}, + "outputs": [], + "source": [ + "plot_branch_vs_others(1)" + ] + }, + { + "cell_type": "markdown", + "id": "7f2075c3", + "metadata": {}, + "source": [ + "## Computing the periodic orbits (POs) out of the fixed point" + ] + }, + { + "cell_type": "markdown", + "id": "e00931b3", + "metadata": {}, + "source": [ + "Loading the branch and printing the summary" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b535d9be", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "r = ac.loadbd('fp1')\n", + "print(r.summary())" + ] + }, + { + "cell_type": "markdown", + "id": "12205b02", + "metadata": {}, + "source": [ + "Listing the Hopf bifurcation points" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12261493", + "metadata": {}, + "outputs": [], + "source": [ + "solutions_list = list()\n", + "ps_obj = parseS.parseS('./s.fp1')\n", + "pc_full_obj = parseC.parseC('c.'+model_name)\n", + "for i in range(len(ps_obj)):\n", + " s = ps_obj[i].load(constants=pc_full_obj)\n", + " if s['TY'] == 'HB':\n", + " solutions_list.append(s)\n", + " \n", + "\n", + "# reversing to get it in Co increasing order\n", + "solutions_list = solutions_list[::-1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3932386", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "solutions_list" + ] + }, + { + "cell_type": "markdown", + "id": "91199f37", + "metadata": {}, + "source": [ + "### Computing and plotting the second Hopf bifurcation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2604fb2", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "s = solutions_list[1]\n", + "rh=ac.run(s,ICP=['C_go1', 'T'], IPS=2, NTST=400, runner=runner)\n", + "ac.save(rh, 'fp1_hp1')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0593b6a8", + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "ax, _ = plot_branches('./b.fp1_hp1', variables=(0, 3))\n", + "plot_branches('./b.fp1', ax=ax, variables=(0, 3))" + ] + }, + { + "cell_type": "markdown", + "id": "9b8f7205", + "metadata": {}, + "source": [ + "Other fixed point and periodic orbit branches can of course be computed in the same way." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/symbolic_output_land_atmosphere.ipynb b/notebooks/symbolic_output_land_atmosphere.ipynb index 348a492..822fe14 100644 --- a/notebooks/symbolic_output_land_atmosphere.ipynb +++ b/notebooks/symbolic_output_land_atmosphere.ipynb @@ -65,8 +65,7 @@ "outputs": [], "source": [ "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "from mpl_toolkits.mplot3d import Axes3D" + "import matplotlib.pyplot as plt" ] }, { @@ -248,7 +247,7 @@ "id": "8ddc3286", "metadata": {}, "source": [ - "Calculating the tendencies in Python as a function of the parameters $C_{{\\rm o},0}$, $C_{{\\rm a},0}$, $k_d$ and $k'_d$:" + "Calculating the tendencies in Python as a function of the parameters $C_{{\\rm g},0}$, $C_{{\\rm a},0}$, $k_d$ and $k'_d$:" ] }, { From b0bb3f34f2a0fd846b0ca56faf0b55f8164ff9a2 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 11 Mar 2025 17:38:38 +0100 Subject: [PATCH 141/143] Moving the symbolic outputs notebook to a given folder --- .../symbolic_output_land_atmosphere-AUTO.ipynb | 0 .../{ => symbolic_outputs}/symbolic_output_land_atmosphere.ipynb | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename notebooks/{ => symbolic_outputs}/symbolic_output_land_atmosphere-AUTO.ipynb (100%) rename notebooks/{ => symbolic_outputs}/symbolic_output_land_atmosphere.ipynb (100%) diff --git a/notebooks/symbolic_output_land_atmosphere-AUTO.ipynb b/notebooks/symbolic_outputs/symbolic_output_land_atmosphere-AUTO.ipynb similarity index 100% rename from notebooks/symbolic_output_land_atmosphere-AUTO.ipynb rename to notebooks/symbolic_outputs/symbolic_output_land_atmosphere-AUTO.ipynb diff --git a/notebooks/symbolic_output_land_atmosphere.ipynb b/notebooks/symbolic_outputs/symbolic_output_land_atmosphere.ipynb similarity index 100% rename from notebooks/symbolic_output_land_atmosphere.ipynb rename to notebooks/symbolic_outputs/symbolic_output_land_atmosphere.ipynb From af2aac5cb9107133ebc18b24e85c0ebd3da7e239 Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Tue, 11 Mar 2025 18:05:19 +0100 Subject: [PATCH 142/143] Cleaning again --- .../Symbolic Output-Linear-ground-AUTO.ipynb | 725 ------------------ 1 file changed, 725 deletions(-) delete mode 100644 notebooks/Symbolic Output-Linear-ground-AUTO.ipynb diff --git a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb b/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb deleted file mode 100644 index 0bf2859..0000000 --- a/notebooks/Symbolic Output-Linear-ground-AUTO.ipynb +++ /dev/null @@ -1,725 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "037e82a6", - "metadata": {}, - "source": [ - "# Linear symbolic Land-atmosphere example " - ] - }, - { - "cell_type": "markdown", - "id": "57e1a1bb", - "metadata": {}, - "source": [ - "Testing platform for the symbolic equation version of the qgs model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12f59a3f", - "metadata": {}, - "outputs": [], - "source": [ - "import sys, os\n", - "import glob\n", - "sys.path.extend([os.path.abspath('../')])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9b3814d5", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7711e102", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.params.params import QgParams\n", - "from qgs.functions.tendencies import create_tendencies" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8500ea89", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.inner_products.symbolic import AtmosphericSymbolicInnerProducts, GroundSymbolicInnerProducts\n", - "from qgs.tensors.qgtensor import QgsTensor, QgsTensorDynamicT, QgsTensorT4\n", - "from qgs.tensors.symbolic_qgtensor import SymbolicQgsTensor, SymbolicQgsTensorDynamicT" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7e03aa6e", - "metadata": {}, - "outputs": [], - "source": [ - "from qgs.functions.symbolic_tendencies import create_symbolic_equations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "bf632fac", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters = QgParams({'phi0_npi': np.deg2rad(50.)/np.pi, 'n':1.3 }, dynamic_T=False)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2a8e7c1", - "metadata": {}, - "outputs": [], - "source": [ - "model_parameters.set_atmospheric_channel_fourier_modes(2, 2, mode=\"symbolic\")\n", - "# Mode truncation at the wavenumber 2 in the x and at the \n", - "# wavenumber 4 in the y spatial coordinates for the ocean\n", - "model_parameters.set_ground_channel_fourier_modes(2, 2, mode=\"symbolic\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3237dc76", - "metadata": {}, - "outputs": [], - "source": [ - "# Changing (increasing) the orography depth\n", - "model_parameters.ground_params.set_orography(0.2, 1)\n", - "# Setting the parameters of the heat transfer from the soil\n", - "model_parameters.gotemperature_params.set_params({'gamma': 1.6e7, 'T0': 300})\n", - "model_parameters.atemperature_params.set_params({ 'hlambda':10, 'T0': 290})\n", - "# Setting atmospheric parameters\n", - "model_parameters.atmospheric_params.set_params({'sigma': 0.2, 'kd': 0.085, 'kdp': 0.02})\n", - "\n", - "# Setting insolation \n", - "model_parameters.gotemperature_params.set_params({})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "51907767", - "metadata": {}, - "outputs": [], - "source": [ - "C_g = 300\n", - "model_parameters.atemperature_params.set_insolation(0.4*C_g , 0)\n", - "\n", - "model_parameters.gotemperature_params.set_insolation(C_g , 0)" - ] - }, - { - "cell_type": "markdown", - "id": "da46eccd", - "metadata": {}, - "source": [ - "## Creating AUTO files" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "af1b4747", - "metadata": {}, - "outputs": [], - "source": [ - "funcs, = create_symbolic_equations(model_parameters, continuation_variables=[model_parameters.gotemperature_params.C[0], model_parameters.atemperature_params.C[0], model_parameters.atmospheric_params.kd, model_parameters.atmospheric_params.kdp], language='auto')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "87f86011", - "metadata": {}, - "outputs": [], - "source": [ - "auto_eq_lines = funcs[0].split('\\n')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ec60bcec", - "metadata": {}, - "outputs": [], - "source": [ - "for i, line in enumerate(auto_eq_lines):\n", - " if 'C_a1 = PAR(2)' in line:\n", - " auto_eq_lines[i] = '\\tC_a1 = 0.4*C_go1'\n", - " break" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "626c1862", - "metadata": {}, - "outputs": [], - "source": [ - "auto_eq = '\\n'.join(auto_eq_lines)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "78c7ceba", - "metadata": {}, - "outputs": [], - "source": [ - "auto_config_lines = funcs[1].split('\\n')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "065e83b3", - "metadata": {}, - "outputs": [], - "source": [ - "for i, line in enumerate(auto_config_lines):\n", - " if 'UZR' in line:\n", - " auto_config_lines[i] = \"UZR = {'C_go1': \" + str(list(np.arange(50.,375.,50.)))+\"}\"\n", - " break" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cc7b79fe", - "metadata": {}, - "outputs": [], - "source": [ - "for i, line in enumerate(auto_config_lines):\n", - " if 'UZSTOP' in line:\n", - " auto_config_lines[i] = \"UZSTOP = {'C_go1': [0.,400.]}\"\n", - " break" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f7760092", - "metadata": {}, - "outputs": [], - "source": [ - "auto_config = '\\n'.join(auto_config_lines)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "80f05363", - "metadata": {}, - "outputs": [], - "source": [ - "with open('qgs_land-atmosphere_auto.f90', 'w') as ff:\n", - " ff.write(auto_eq)\n", - " \n", - "with open('c.qgs_land-atmosphere_auto', 'w') as ff:\n", - " ff.write(auto_config)" - ] - }, - { - "cell_type": "markdown", - "id": "3f219527", - "metadata": {}, - "source": [ - "## Defining some useful functions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "29fe26ab", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_branches(filename, variables=(0,1), ax=None, figsize=(10, 8), markersize=12., plot_kwargs=None, marker_kwargs=None, branch_indices='all', excluded_labels=('UZ', 'EP', 'No Label'), variables_name=None):\n", - " \n", - " if ax is None:\n", - " fig = plt.figure(figsize=figsize)\n", - " ax = fig.gca()\n", - " \n", - " if plot_kwargs is None:\n", - " plot_kwargs = dict()\n", - " \n", - " if marker_kwargs is None:\n", - " marker_kwargs = dict()\n", - " \n", - " pb_obj = parseB.parseB()\n", - " fb = open(filename, 'r')\n", - " pb_obj.read(fb)\n", - " \n", - " keys = list(pb_obj.branches[0].keys())\n", - " \n", - " if variables[0] in keys:\n", - " var1 = variables[0]\n", - " else:\n", - " try:\n", - " var1 = keys[variables[0]]\n", - " except:\n", - " var1 = keys[0]\n", - "\n", - " if variables[1] in keys:\n", - " var2 = variables[1]\n", - " else:\n", - " try:\n", - " var2 = keys[variables[1]]\n", - " except:\n", - " var2 = keys[1]\n", - "\n", - " if branch_indices == 'all':\n", - " branch_indices = range(len(pb_obj.branches))\n", - "\n", - " branch_num = list()\n", - " for i in branch_indices:\n", - " branch_dict = pb_obj.branches[i].todict()\n", - " branch_num.append(pb_obj.branches[i]['BR'])\n", - "\n", - " labels = list()\n", - " for j, coords in enumerate(zip(branch_dict[var1], branch_dict[var2])):\n", - " lab = pb_obj.branches[i].labels[j]\n", - " if not lab:\n", - " pass\n", - " else:\n", - " labels.append((coords, list(lab.keys())[0]))\n", - "\n", - " ax.plot(branch_dict[var1], branch_dict[var2], **plot_kwargs)\n", - " if excluded_labels != 'all':\n", - " for label in labels:\n", - " coords = label[0]\n", - " lab = label[1]\n", - " if lab not in excluded_labels:\n", - " ax.text(coords[0], coords[1], r'${\\bf '+ lab + r'}$', fontdict={'family':'sans-serif','size':markersize},va='center', ha='center', **marker_kwargs, clip_on=True)\n", - " \n", - " fb.close()\n", - " if variables_name is None:\n", - " ax.set_xlabel(var1)\n", - " ax.set_ylabel(var2)\n", - " else:\n", - " if isinstance(variables_name, dict):\n", - " ax.set_xlabel(variables_name[var1])\n", - " ax.set_ylabel(variables_name[var2])\n", - " else:\n", - " ax.set_xlabel(variables_name[0])\n", - " ax.set_ylabel(variables_name[1])\n", - " return ax, branch_num" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c1b0d5cb", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_branches3d(filename, variables=(0,1,3), ax=None, figsize=(10, 8), markersize=12., plot_kwargs=None, marker_kwargs=None, branch_indices='all', excluded_labels=('UZ', 'EP', 'No Label'), variables_name=None):\n", - " \n", - " if ax is None:\n", - " fig = plt.figure(figsize=figsize)\n", - " ax = plt.subplot(projection='3d')\n", - " \n", - " if plot_kwargs is None:\n", - " plot_kwargs = dict()\n", - " \n", - " if marker_kwargs is None:\n", - " marker_kwargs = dict()\n", - " \n", - " pb_obj = parseB.parseB()\n", - " fb = open(filename, 'r')\n", - " pb_obj.read(fb)\n", - " \n", - " keys = list(pb_obj.branches[0].keys())\n", - " \n", - " if variables[0] in keys:\n", - " var1 = variables[0]\n", - " else:\n", - " try:\n", - " var1 = keys[variables[0]]\n", - " except:\n", - " var1 = keys[0]\n", - "\n", - " if variables[1] in keys:\n", - " var2 = variables[1]\n", - " else:\n", - " try:\n", - " var2 = keys[variables[1]]\n", - " except:\n", - " var2 = keys[1]\n", - " \n", - " if variables[2] in keys:\n", - " var3 = variables[2]\n", - " else:\n", - " try:\n", - " var3 = keys[variables[2]]\n", - " except:\n", - " var3 = keys[2]\n", - "\n", - "\n", - " if branch_indices == 'all':\n", - " branch_indices = range(len(pb_obj.branches))\n", - "\n", - " branch_num = list()\n", - " for i in branch_indices:\n", - " branch_dict = pb_obj.branches[i].todict()\n", - " branch_num.append(pb_obj.branches[i]['BR'])\n", - "\n", - " labels = list()\n", - " for j, coords in enumerate(zip(branch_dict[var1], branch_dict[var2], branch_dict[var3])):\n", - " lab = pb_obj.branches[i].labels[j]\n", - " if not lab:\n", - " pass\n", - " else:\n", - " labels.append((coords, list(lab.keys())[0]))\n", - "\n", - " ax.plot(branch_dict[var1], branch_dict[var2], branch_dict[var3], **plot_kwargs)\n", - " if excluded_labels != 'all':\n", - " for label in labels:\n", - " coords = label[0]\n", - " lab = label[1]\n", - " if lab not in excluded_labels:\n", - " ax.text(coords[0], coords[1], coords[2], r'${\\bf '+ lab + r'}$', fontdict={'family':'sans-serif','size':markersize},va='center', ha='center', **marker_kwargs, clip_on=True)\n", - " \n", - " fb.close()\n", - " if variables_name is None:\n", - " ax.set_xlabel(var1)\n", - " ax.set_ylabel(var2)\n", - " ax.set_zlabel(var3)\n", - " else:\n", - " if isinstance(variables_name, dict):\n", - " ax.set_xlabel(variables_name[var1])\n", - " ax.set_ylabel(variables_name[var2])\n", - " ax.set_zlabel(variables_name[var3])\n", - " else:\n", - " ax.set_xlabel(variables_name[0])\n", - " ax.set_ylabel(variables_name[1])\n", - " ax.set_zlabel(variables_name[2])\n", - " return ax, branch_num" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ff2a41a0", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_branch_vs_others(branch_num, figsize=(10, 16), excluded_labels=('UZ', 'EP', 'No Label')):\n", - " \n", - " fig = plt.figure(figsize=figsize)\n", - " ax = plt.subplot(2,1,1)\n", - " ax3 = plt.subplot(2,1,2, projection='3d')\n", - " \n", - " \n", - " \n", - " fp = glob.glob(nb_dir + '/b.fp*')\n", - " fp = [item for item in fp if '~' not in os.path.basename(item)]\n", - " fp = [item for item in fp if '_' not in os.path.basename(item)]\n", - " \n", - " for i in range(len(fp)-1,-1,-1):\n", - " \n", - " try:\n", - " num = int(fp[i][-2:])\n", - " except:\n", - " num = int(fp[i][-1])\n", - " \n", - " if num == branch_num:\n", - " plot_branches(fp[i], ax=ax, plot_kwargs={'color': 'tab:blue', 'zorder': 10.}, variables=(0, 1), variables_name=(r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=excluded_labels)\n", - " plot_branches3d(fp[i], ax=ax3, plot_kwargs={'color': 'tab:blue', 'zorder': 10.}, variables=(3, 0, 1), variables_name=(r'$\\psi_{{\\rm a}, 2}$', r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=excluded_labels)\n", - " else:\n", - " plot_branches(fp[i], ax=ax, plot_kwargs={'color': 'tab:orange'}, variables=(0, 1), variables_name=(r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=\"all\")\n", - " plot_branches3d(fp[i], ax=ax3, plot_kwargs={'color': 'tab:orange'}, variables=(3, 0, 1), variables_name=(r'$\\psi_{{\\rm a}, 2}$', r'$C_{\\rm o}$', r'$L_2$ norm'), excluded_labels=\"all\")\n", - "\n", - " " - ] - }, - { - "cell_type": "markdown", - "id": "2d4e97e3", - "metadata": {}, - "source": [ - "## AUTO analysis" - ] - }, - { - "cell_type": "markdown", - "id": "613647e4", - "metadata": {}, - "source": [ - "Initializing AUTO" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0b291604", - "metadata": {}, - "outputs": [], - "source": [ - "auto_directory = os.environ['AUTO_DIR']" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a70a505f", - "metadata": {}, - "outputs": [], - "source": [ - "sys.path.append(auto_directory + '/python/auto')\n", - "sys.path.append(auto_directory + '/python')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d1ddf964", - "metadata": {}, - "outputs": [], - "source": [ - "import AUTOCommands as ac\n", - "import AUTOclui as acl\n", - "import interactiveBindings as ib\n", - "import runAUTO as ra\n", - "import parseB, parseC, parseD, parseS, parseBandS" - ] - }, - { - "cell_type": "markdown", - "id": "a30354af", - "metadata": {}, - "source": [ - "Loading the model" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2700819a", - "metadata": {}, - "outputs": [], - "source": [ - "nb_dir = !pwd\n", - "nb_dir = nb_dir[0]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d4f451e7", - "metadata": {}, - "outputs": [], - "source": [ - "lf=glob.glob(nb_dir + '/c.*')\n", - "lf = [item for item in lf if '~' not in item]\n", - "\n", - "y=lf[0]\n", - "mname=y[len(nb_dir)+3:]\n", - "print(\"Loading model \"+mname)" - ] - }, - { - "cell_type": "markdown", - "id": "539f2d00", - "metadata": {}, - "source": [ - "Starting a runner" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1240ace5", - "metadata": {}, - "outputs": [], - "source": [ - "runner = ra.runAUTO()\n", - "ac.load(mname, runner=runner)" - ] - }, - { - "cell_type": "markdown", - "id": "672ef822", - "metadata": {}, - "source": [ - "Finding the first branch of fixed point" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fe0c0600", - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "U_dic = {i+1: 0. for i in range(model_parameters.ndim)}\n", - "x = ac.run(mname, U=U_dic, ICP=['C_go1'], PAR={3: model_parameters.atmospheric_params.kd, 4: model_parameters.atmospheric_params.kdp}, runner=runner)\n", - "ac.save(x,'fp1')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "375f5335", - "metadata": {}, - "outputs": [], - "source": [ - "plot_branch_vs_others(1)" - ] - }, - { - "cell_type": "markdown", - "id": "7f2075c3", - "metadata": {}, - "source": [ - "## Computing the periodic orbits (POs) out of the fixed point" - ] - }, - { - "cell_type": "markdown", - "id": "e00931b3", - "metadata": {}, - "source": [ - "Loading the branch and printing the summary" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b535d9be", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "r = ac.loadbd('fp1')\n", - "print(r.summary())" - ] - }, - { - "cell_type": "markdown", - "id": "12205b02", - "metadata": {}, - "source": [ - "Listing the Hopf bifurcation points" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "12261493", - "metadata": {}, - "outputs": [], - "source": [ - "solutions_list = list()\n", - "ps_obj = parseS.parseS('./s.fp1')\n", - "pc_full_obj = parseC.parseC('c.'+mname)\n", - "for i in range(len(ps_obj)):\n", - " s = ps_obj[i].load(constants=pc_full_obj)\n", - " if s['TY'] == 'HB':\n", - " solutions_list.append(s)\n", - " \n", - "\n", - "# reversing to get it in Co increasing order\n", - "solutions_list = solutions_list[::-1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f3932386", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "solutions_list" - ] - }, - { - "cell_type": "markdown", - "id": "91199f37", - "metadata": {}, - "source": [ - "### Computing the second Hopf bifurcation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "a2604fb2", - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "s = solutions_list[1]\n", - "rh=ac.run(s,ICP=['C_go1', 'T'], IPS=2, NTST=400, runner=runner)\n", - "ac.save(rh, 'fp1_hp1')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "0593b6a8", - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "ax, _ = plot_branches('./b.fp1_hp1', variables=(0, 3))\n", - "plot_branches('./b.fp1', ax=ax, variables=(0, 3))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "190b8b53", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.8.16" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 61eaa03fecde2fb33552b59bb8dd28f9947d06ee Mon Sep 17 00:00:00 2001 From: Jonathan Demaeyer Date: Wed, 12 Mar 2025 11:12:05 +0100 Subject: [PATCH 143/143] Solved a bug that prevented reuse of the test classes --- model_test/test_base_symbolic.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/model_test/test_base_symbolic.py b/model_test/test_base_symbolic.py index 732184d..b540410 100644 --- a/model_test/test_base_symbolic.py +++ b/model_test/test_base_symbolic.py @@ -37,29 +37,25 @@ def save_ip_numeric(self, s): self.numerical_values.append(s) def check_lists_flt(self): - if len(self.symbolic_values) == 0: - self.symbolic_outputs() + self.symbolic_outputs() self.load_ref_from_file() for v, r in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.reference)))): self.assertTrue(self.match_flt(v, r), msg=v+' != '+r+' !!!') def check_lists(self, cmax=1): - if len(self.symbolic_values) == 0: - self.symbolic_outputs() + self.symbolic_outputs() self.load_ref_from_file() for v, r in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.reference)))): self.assertTrue(self.match_str(v, r, cmax), msg=v+' != '+r+' !!!') def check_numerical_lists_flt(self): - if len(self.symbolic_values) == 0: - self.symbolic_outputs() + self.symbolic_outputs() self.numerical_outputs() for v, r in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.numerical_values)))): self.assertTrue(self.match_flt(v, r), msg=v+' != '+r+' !!!') def check_numerical_lists(self, cmax=1): - if len(self.symbolic_values) == 0: - self.symbolic_outputs() + self.symbolic_outputs() self.numerical_outputs() for v, r, in zip(list(reversed(sorted(self.symbolic_values))), list(reversed(sorted(self.numerical_values)))): self.assertTrue(self.match_str(v, r, cmax), msg=v+' != '+r+' !!!')