From 2273bf8129cb81554812a74cea281a156636d4bf Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Fri, 26 May 2023 15:58:04 +0200 Subject: [PATCH 01/61] first commit for implementing accurate self field --- src/simsopt/field/biotsavart_mod.py | 229 ++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 src/simsopt/field/biotsavart_mod.py diff --git a/src/simsopt/field/biotsavart_mod.py b/src/simsopt/field/biotsavart_mod.py new file mode 100644 index 000000000..ca2ce5d81 --- /dev/null +++ b/src/simsopt/field/biotsavart_mod.py @@ -0,0 +1,229 @@ +import json +import numpy as np + +import simsoptpp as sopp +from .magneticfield import MagneticField +from .._core.json import GSONable, GSONDecoder, GSONEncoder + +__all__ = ['BiotSavart'] + + +class BiotSavart(sopp.BiotSavart, MagneticField): + r""" + Computes the MagneticField induced by a list of closed curves :math:`\Gamma_k` with electric currents :math:`I_k`. + The field is given by + + .. math:: + + B(\mathbf{x}) = \frac{\mu_0}{4\pi} \sum_{k=1}^{n_\mathrm{coils}} I_k \int_0^1 \frac{(\Gamma_k(\phi)-\mathbf{x})\times \Gamma_k'(\phi)}{\|\Gamma_k(\phi)-\mathbf{x}\|^3} d\phi + + where :math:`\mu_0=4\pi 10^{-7}` is the magnetic constant. + + Args: + coils: A list of :obj:`simsopt.field.coil.Coil` objects. + """ + + def __init__(self, coils): + self._coils = coils + sopp.BiotSavart.__init__(self, coils) + MagneticField.__init__(self, depends_on=coils) + + def dB_by_dcoilcurrents(self, compute_derivatives=0): + points = self.get_points_cart_ref() + npoints = len(points) + ncoils = len(self._coils) + if any([not self.fieldcache_get_status(f'B_{i}') for i in range(ncoils)]): + assert compute_derivatives >= 0 + self.compute(compute_derivatives) + self._dB_by_dcoilcurrents = [self.fieldcache_get_or_create(f'B_{i}', [npoints, 3]) for i in range(ncoils)] + return self._dB_by_dcoilcurrents + + def d2B_by_dXdcoilcurrents(self, compute_derivatives=1): + points = self.get_points_cart_ref() + npoints = len(points) + ncoils = len(self._coils) + if any([not self.fieldcache_get_status(f'dB_{i}') for i in range(ncoils)]): + assert compute_derivatives >= 1 + self.compute(compute_derivatives) + self._d2B_by_dXdcoilcurrents = [self.fieldcache_get_or_create(f'dB_{i}', [npoints, 3, 3]) for i in range(ncoils)] + return self._d2B_by_dXdcoilcurrents + + def d3B_by_dXdXdcoilcurrents(self, compute_derivatives=2): + points = self.get_points_cart_ref() + npoints = len(points) + ncoils = len(self._coils) + if any([not self.fieldcache_get_status(f'ddB_{i}') for i in range(ncoils)]): + assert compute_derivatives >= 2 + self.compute(compute_derivatives) + self._d3B_by_dXdXdcoilcurrents = [self.fieldcache_get_or_create(f'ddB_{i}', [npoints, 3, 3, 3]) for i in range(ncoils)] + return self._d3B_by_dXdXdcoilcurrents + + def B_and_dB_vjp(self, v, vgrad): + r""" + Same as :obj:`simsopt.geo.biotsavart.BiotSavart.B_vjp` but returns the vector Jacobian product for :math:`B` and :math:`\nabla B`, i.e. it returns + + .. math:: + + \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{B}_i \}_k, \{ \sum_{i=1}^{n} {\mathbf{v}_\mathrm{grad}}_i \cdot \partial_{\mathbf{c}_k} \nabla \mathbf{B}_i \}_k. + """ + + coils = self._coils + gammas = [coil.curve.gamma() for coil in coils] + gammadashs = [coil.curve.gammadash() for coil in coils] + currents = [coil.current.get_value() for coil in coils] + res_gamma = [np.zeros_like(gamma) for gamma in gammas] + res_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] + res_grad_gamma = [np.zeros_like(gamma) for gamma in gammas] + res_grad_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] + + points = self.get_points_cart_ref() + sopp.biot_savart_vjp_graph(points, gammas, gammadashs, currents, v, + res_gamma, res_gammadash, vgrad, res_grad_gamma, res_grad_gammadash) + + dB_by_dcoilcurrents = self.dB_by_dcoilcurrents() + res_current = [np.sum(v * dB_by_dcoilcurrents[i]) for i in range(len(dB_by_dcoilcurrents))] + d2B_by_dXdcoilcurrents = self.d2B_by_dXdcoilcurrents() + res_grad_current = [np.sum(vgrad * d2B_by_dXdcoilcurrents[i]) for i in range(len(d2B_by_dXdcoilcurrents))] + + res = ( + sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]), + sum([coils[i].vjp(res_grad_gamma[i], res_grad_gammadash[i], np.asarray([res_grad_current[i]])) for i in range(len(coils))]) + ) + + return res + + def B_vjp(self, v): + r""" + Assume the field was evaluated at points :math:`\mathbf{x}_i, i\in \{1, \ldots, n\}` and denote the value of the field at those points by + :math:`\{\mathbf{B}_i\}_{i=1}^n`. + These values depend on the shape of the coils, i.e. on the dofs :math:`\mathbf{c}_k` of each coil. + This function returns the vector Jacobian product of this dependency, i.e. + + .. math:: + + \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{B}_i \}_k. + + """ + + coils = self._coils + gammas = [coil.curve.gamma() for coil in coils] + gammadashs = [coil.curve.gammadash() for coil in coils] + currents = [coil.current.get_value() for coil in coils] + res_gamma = [np.zeros_like(gamma) for gamma in gammas] + res_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] + + points = self.get_points_cart_ref() + sopp.biot_savart_vjp_graph(points, gammas, gammadashs, currents, v, + res_gamma, res_gammadash, [], [], []) + dB_by_dcoilcurrents = self.dB_by_dcoilcurrents() + res_current = [np.sum(v * dB_by_dcoilcurrents[i]) for i in range(len(dB_by_dcoilcurrents))] + return sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]) + + def dA_by_dcoilcurrents(self, compute_derivatives=0): + points = self.get_points_cart_ref() + npoints = len(points) + ncoils = len(self._coils) + if any([not self.fieldcache_get_status(f'A_{i}') for i in range(ncoils)]): + assert compute_derivatives >= 0 + self.compute(compute_derivatives) + self._dA_by_dcoilcurrents = [self.fieldcache_get_or_create(f'A_{i}', [npoints, 3]) for i in range(ncoils)] + return self._dA_by_dcoilcurrents + + def d2A_by_dXdcoilcurrents(self, compute_derivatives=1): + points = self.get_points_cart_ref() + npoints = len(points) + ncoils = len(self._coils) + if any([not self.fieldcache_get_status(f'dA_{i}') for i in range(ncoils)]): + assert compute_derivatives >= 1 + self.compute(compute_derivatives) + self._d2A_by_dXdcoilcurrents = [self.fieldcache_get_or_create(f'dA_{i}', [npoints, 3, 3]) for i in range(ncoils)] + return self._d2A_by_dXdcoilcurrents + + def d3A_by_dXdXdcoilcurrents(self, compute_derivatives=2): + points = self.get_points_cart_ref() + npoints = len(points) + ncoils = len(self._coils) + if any([not self.fieldcache_get_status(f'ddA_{i}') for i in range(ncoils)]): + assert compute_derivatives >= 2 + self.compute(compute_derivatives) + self._d3A_by_dXdXdcoilcurrents = [self.fieldcache_get_or_create(f'ddA_{i}', [npoints, 3, 3, 3]) for i in range(ncoils)] + return self._d3A_by_dXdXdcoilcurrents + + def A_and_dA_vjp(self, v, vgrad): + r""" + Same as :obj:`simsopt.geo.biotsavart.BiotSavart.A_vjp` but returns the vector Jacobian product for :math:`A` and :math:`\nabla A`, i.e. it returns + + .. math:: + + \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{A}_i \}_k, \{ \sum_{i=1}^{n} {\mathbf{v}_\mathrm{grad}}_i \cdot \partial_{\mathbf{c}_k} \nabla \mathbf{A}_i \}_k. + """ + + coils = self._coils + gammas = [coil.curve.gamma() for coil in coils] + gammadashs = [coil.curve.gammadash() for coil in coils] + currents = [coil.current.get_value() for coil in coils] + res_gamma = [np.zeros_like(gamma) for gamma in gammas] + res_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] + res_grad_gamma = [np.zeros_like(gamma) for gamma in gammas] + res_grad_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] + + points = self.get_points_cart_ref() + sopp.biot_savart_vector_potential_vjp_graph(points, gammas, gammadashs, currents, v, + res_gamma, res_gammadash, vgrad, res_grad_gamma, res_grad_gammadash) + + dA_by_dcoilcurrents = self.dA_by_dcoilcurrents() + res_current = [np.sum(v * dA_by_dcoilcurrents[i]) for i in range(len(dA_by_dcoilcurrents))] + d2A_by_dXdcoilcurrents = self.d2A_by_dXdcoilcurrents() + res_grad_current = [np.sum(vgrad * d2A_by_dXdcoilcurrents[i]) for i in range(len(d2A_by_dXdcoilcurrents))] + + res = ( + sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]), + sum([coils[i].vjp(res_grad_gamma[i], res_grad_gammadash[i], np.asarray([res_grad_current[i]])) for i in range(len(coils))]) + ) + + return res + + def A_vjp(self, v): + r""" + Assume the field was evaluated at points :math:`\mathbf{x}_i, i\in \{1, \ldots, n\}` and denote the value of the field at those points by + :math:`\{\mathbf{A}_i\}_{i=1}^n`. + These values depend on the shape of the coils, i.e. on the dofs :math:`\mathbf{c}_k` of each coil. + This function returns the vector Jacobian product of this dependency, i.e. + + .. math:: + + \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{A}_i \}_k. + + """ + + coils = self._coils + gammas = [coil.curve.gamma() for coil in coils] + gammadashs = [coil.curve.gammadash() for coil in coils] + currents = [coil.current.get_value() for coil in coils] + res_gamma = [np.zeros_like(gamma) for gamma in gammas] + res_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] + + points = self.get_points_cart_ref() + sopp.biot_savart_vector_potential_vjp_graph(points, gammas, gammadashs, currents, v, + res_gamma, res_gammadash, [], [], []) + dA_by_dcoilcurrents = self.dA_by_dcoilcurrents() + res_current = [np.sum(v * dA_by_dcoilcurrents[i]) for i in range(len(dA_by_dcoilcurrents))] + return sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]) + + def as_dict(self, serial_objs_dict) -> dict: + d = super().as_dict(serial_objs_dict=serial_objs_dict) + d["points"] = self.get_points_cart() + return d + + @classmethod + def from_dict(cls, d, serial_objs_dict, recon_objs): + decoder = GSONDecoder() + xyz = decoder.process_decoded(d["points"], + serial_objs_dict=serial_objs_dict, + recon_objs=recon_objs) + coils = decoder.process_decoded(d["coils"], + serial_objs_dict=serial_objs_dict, + recon_objs=recon_objs) + bs = cls(coils) + bs.set_points_cart(xyz) + return bs From 5ab839b838aaf89a3443763b4a532594d8529710 Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Tue, 6 Jun 2023 15:58:31 +0200 Subject: [PATCH 02/61] First commit on coil_forces branch --- src/simsopt/field/biotsavart_mod.py | 230 +--------------------------- 1 file changed, 1 insertion(+), 229 deletions(-) diff --git a/src/simsopt/field/biotsavart_mod.py b/src/simsopt/field/biotsavart_mod.py index ca2ce5d81..08ce7bcb4 100644 --- a/src/simsopt/field/biotsavart_mod.py +++ b/src/simsopt/field/biotsavart_mod.py @@ -1,229 +1 @@ -import json -import numpy as np - -import simsoptpp as sopp -from .magneticfield import MagneticField -from .._core.json import GSONable, GSONDecoder, GSONEncoder - -__all__ = ['BiotSavart'] - - -class BiotSavart(sopp.BiotSavart, MagneticField): - r""" - Computes the MagneticField induced by a list of closed curves :math:`\Gamma_k` with electric currents :math:`I_k`. - The field is given by - - .. math:: - - B(\mathbf{x}) = \frac{\mu_0}{4\pi} \sum_{k=1}^{n_\mathrm{coils}} I_k \int_0^1 \frac{(\Gamma_k(\phi)-\mathbf{x})\times \Gamma_k'(\phi)}{\|\Gamma_k(\phi)-\mathbf{x}\|^3} d\phi - - where :math:`\mu_0=4\pi 10^{-7}` is the magnetic constant. - - Args: - coils: A list of :obj:`simsopt.field.coil.Coil` objects. - """ - - def __init__(self, coils): - self._coils = coils - sopp.BiotSavart.__init__(self, coils) - MagneticField.__init__(self, depends_on=coils) - - def dB_by_dcoilcurrents(self, compute_derivatives=0): - points = self.get_points_cart_ref() - npoints = len(points) - ncoils = len(self._coils) - if any([not self.fieldcache_get_status(f'B_{i}') for i in range(ncoils)]): - assert compute_derivatives >= 0 - self.compute(compute_derivatives) - self._dB_by_dcoilcurrents = [self.fieldcache_get_or_create(f'B_{i}', [npoints, 3]) for i in range(ncoils)] - return self._dB_by_dcoilcurrents - - def d2B_by_dXdcoilcurrents(self, compute_derivatives=1): - points = self.get_points_cart_ref() - npoints = len(points) - ncoils = len(self._coils) - if any([not self.fieldcache_get_status(f'dB_{i}') for i in range(ncoils)]): - assert compute_derivatives >= 1 - self.compute(compute_derivatives) - self._d2B_by_dXdcoilcurrents = [self.fieldcache_get_or_create(f'dB_{i}', [npoints, 3, 3]) for i in range(ncoils)] - return self._d2B_by_dXdcoilcurrents - - def d3B_by_dXdXdcoilcurrents(self, compute_derivatives=2): - points = self.get_points_cart_ref() - npoints = len(points) - ncoils = len(self._coils) - if any([not self.fieldcache_get_status(f'ddB_{i}') for i in range(ncoils)]): - assert compute_derivatives >= 2 - self.compute(compute_derivatives) - self._d3B_by_dXdXdcoilcurrents = [self.fieldcache_get_or_create(f'ddB_{i}', [npoints, 3, 3, 3]) for i in range(ncoils)] - return self._d3B_by_dXdXdcoilcurrents - - def B_and_dB_vjp(self, v, vgrad): - r""" - Same as :obj:`simsopt.geo.biotsavart.BiotSavart.B_vjp` but returns the vector Jacobian product for :math:`B` and :math:`\nabla B`, i.e. it returns - - .. math:: - - \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{B}_i \}_k, \{ \sum_{i=1}^{n} {\mathbf{v}_\mathrm{grad}}_i \cdot \partial_{\mathbf{c}_k} \nabla \mathbf{B}_i \}_k. - """ - - coils = self._coils - gammas = [coil.curve.gamma() for coil in coils] - gammadashs = [coil.curve.gammadash() for coil in coils] - currents = [coil.current.get_value() for coil in coils] - res_gamma = [np.zeros_like(gamma) for gamma in gammas] - res_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] - res_grad_gamma = [np.zeros_like(gamma) for gamma in gammas] - res_grad_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] - - points = self.get_points_cart_ref() - sopp.biot_savart_vjp_graph(points, gammas, gammadashs, currents, v, - res_gamma, res_gammadash, vgrad, res_grad_gamma, res_grad_gammadash) - - dB_by_dcoilcurrents = self.dB_by_dcoilcurrents() - res_current = [np.sum(v * dB_by_dcoilcurrents[i]) for i in range(len(dB_by_dcoilcurrents))] - d2B_by_dXdcoilcurrents = self.d2B_by_dXdcoilcurrents() - res_grad_current = [np.sum(vgrad * d2B_by_dXdcoilcurrents[i]) for i in range(len(d2B_by_dXdcoilcurrents))] - - res = ( - sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]), - sum([coils[i].vjp(res_grad_gamma[i], res_grad_gammadash[i], np.asarray([res_grad_current[i]])) for i in range(len(coils))]) - ) - - return res - - def B_vjp(self, v): - r""" - Assume the field was evaluated at points :math:`\mathbf{x}_i, i\in \{1, \ldots, n\}` and denote the value of the field at those points by - :math:`\{\mathbf{B}_i\}_{i=1}^n`. - These values depend on the shape of the coils, i.e. on the dofs :math:`\mathbf{c}_k` of each coil. - This function returns the vector Jacobian product of this dependency, i.e. - - .. math:: - - \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{B}_i \}_k. - - """ - - coils = self._coils - gammas = [coil.curve.gamma() for coil in coils] - gammadashs = [coil.curve.gammadash() for coil in coils] - currents = [coil.current.get_value() for coil in coils] - res_gamma = [np.zeros_like(gamma) for gamma in gammas] - res_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] - - points = self.get_points_cart_ref() - sopp.biot_savart_vjp_graph(points, gammas, gammadashs, currents, v, - res_gamma, res_gammadash, [], [], []) - dB_by_dcoilcurrents = self.dB_by_dcoilcurrents() - res_current = [np.sum(v * dB_by_dcoilcurrents[i]) for i in range(len(dB_by_dcoilcurrents))] - return sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]) - - def dA_by_dcoilcurrents(self, compute_derivatives=0): - points = self.get_points_cart_ref() - npoints = len(points) - ncoils = len(self._coils) - if any([not self.fieldcache_get_status(f'A_{i}') for i in range(ncoils)]): - assert compute_derivatives >= 0 - self.compute(compute_derivatives) - self._dA_by_dcoilcurrents = [self.fieldcache_get_or_create(f'A_{i}', [npoints, 3]) for i in range(ncoils)] - return self._dA_by_dcoilcurrents - - def d2A_by_dXdcoilcurrents(self, compute_derivatives=1): - points = self.get_points_cart_ref() - npoints = len(points) - ncoils = len(self._coils) - if any([not self.fieldcache_get_status(f'dA_{i}') for i in range(ncoils)]): - assert compute_derivatives >= 1 - self.compute(compute_derivatives) - self._d2A_by_dXdcoilcurrents = [self.fieldcache_get_or_create(f'dA_{i}', [npoints, 3, 3]) for i in range(ncoils)] - return self._d2A_by_dXdcoilcurrents - - def d3A_by_dXdXdcoilcurrents(self, compute_derivatives=2): - points = self.get_points_cart_ref() - npoints = len(points) - ncoils = len(self._coils) - if any([not self.fieldcache_get_status(f'ddA_{i}') for i in range(ncoils)]): - assert compute_derivatives >= 2 - self.compute(compute_derivatives) - self._d3A_by_dXdXdcoilcurrents = [self.fieldcache_get_or_create(f'ddA_{i}', [npoints, 3, 3, 3]) for i in range(ncoils)] - return self._d3A_by_dXdXdcoilcurrents - - def A_and_dA_vjp(self, v, vgrad): - r""" - Same as :obj:`simsopt.geo.biotsavart.BiotSavart.A_vjp` but returns the vector Jacobian product for :math:`A` and :math:`\nabla A`, i.e. it returns - - .. math:: - - \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{A}_i \}_k, \{ \sum_{i=1}^{n} {\mathbf{v}_\mathrm{grad}}_i \cdot \partial_{\mathbf{c}_k} \nabla \mathbf{A}_i \}_k. - """ - - coils = self._coils - gammas = [coil.curve.gamma() for coil in coils] - gammadashs = [coil.curve.gammadash() for coil in coils] - currents = [coil.current.get_value() for coil in coils] - res_gamma = [np.zeros_like(gamma) for gamma in gammas] - res_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] - res_grad_gamma = [np.zeros_like(gamma) for gamma in gammas] - res_grad_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] - - points = self.get_points_cart_ref() - sopp.biot_savart_vector_potential_vjp_graph(points, gammas, gammadashs, currents, v, - res_gamma, res_gammadash, vgrad, res_grad_gamma, res_grad_gammadash) - - dA_by_dcoilcurrents = self.dA_by_dcoilcurrents() - res_current = [np.sum(v * dA_by_dcoilcurrents[i]) for i in range(len(dA_by_dcoilcurrents))] - d2A_by_dXdcoilcurrents = self.d2A_by_dXdcoilcurrents() - res_grad_current = [np.sum(vgrad * d2A_by_dXdcoilcurrents[i]) for i in range(len(d2A_by_dXdcoilcurrents))] - - res = ( - sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]), - sum([coils[i].vjp(res_grad_gamma[i], res_grad_gammadash[i], np.asarray([res_grad_current[i]])) for i in range(len(coils))]) - ) - - return res - - def A_vjp(self, v): - r""" - Assume the field was evaluated at points :math:`\mathbf{x}_i, i\in \{1, \ldots, n\}` and denote the value of the field at those points by - :math:`\{\mathbf{A}_i\}_{i=1}^n`. - These values depend on the shape of the coils, i.e. on the dofs :math:`\mathbf{c}_k` of each coil. - This function returns the vector Jacobian product of this dependency, i.e. - - .. math:: - - \{ \sum_{i=1}^{n} \mathbf{v}_i \cdot \partial_{\mathbf{c}_k} \mathbf{A}_i \}_k. - - """ - - coils = self._coils - gammas = [coil.curve.gamma() for coil in coils] - gammadashs = [coil.curve.gammadash() for coil in coils] - currents = [coil.current.get_value() for coil in coils] - res_gamma = [np.zeros_like(gamma) for gamma in gammas] - res_gammadash = [np.zeros_like(gammadash) for gammadash in gammadashs] - - points = self.get_points_cart_ref() - sopp.biot_savart_vector_potential_vjp_graph(points, gammas, gammadashs, currents, v, - res_gamma, res_gammadash, [], [], []) - dA_by_dcoilcurrents = self.dA_by_dcoilcurrents() - res_current = [np.sum(v * dA_by_dcoilcurrents[i]) for i in range(len(dA_by_dcoilcurrents))] - return sum([coils[i].vjp(res_gamma[i], res_gammadash[i], np.asarray([res_current[i]])) for i in range(len(coils))]) - - def as_dict(self, serial_objs_dict) -> dict: - d = super().as_dict(serial_objs_dict=serial_objs_dict) - d["points"] = self.get_points_cart() - return d - - @classmethod - def from_dict(cls, d, serial_objs_dict, recon_objs): - decoder = GSONDecoder() - xyz = decoder.process_decoded(d["points"], - serial_objs_dict=serial_objs_dict, - recon_objs=recon_objs) - coils = decoder.process_decoded(d["coils"], - serial_objs_dict=serial_objs_dict, - recon_objs=recon_objs) - bs = cls(coils) - bs.set_points_cart(xyz) - return bs +# self field for force optimization \ No newline at end of file From 1b0a95ce14f0280d5264e1ad7ca907240e1df8ff Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Tue, 6 Jun 2023 16:03:43 +0200 Subject: [PATCH 03/61] initial commit --- src/simsopt/field/self_field.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/simsopt/field/self_field.py diff --git a/src/simsopt/field/self_field.py b/src/simsopt/field/self_field.py new file mode 100644 index 000000000..ba7b17acf --- /dev/null +++ b/src/simsopt/field/self_field.py @@ -0,0 +1 @@ +# self field calculation From 98ab4241688488b378ad2cbf31dc92bc1c46c943 Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Tue, 6 Jun 2023 16:45:54 +0200 Subject: [PATCH 04/61] initial commit for strain optimization --- .../3_Advanced/strain_optimization_script.py | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 examples/3_Advanced/strain_optimization_script.py diff --git a/examples/3_Advanced/strain_optimization_script.py b/examples/3_Advanced/strain_optimization_script.py new file mode 100644 index 000000000..ef57f6ec9 --- /dev/null +++ b/examples/3_Advanced/strain_optimization_script.py @@ -0,0 +1,164 @@ +import os +import numpy as np +import matplotlib.pyplot as plt +from pathlib import Path +from scipy.optimize import minimize +from simsopt.field import BiotSavart +from simsopt.field import Current, Coil, apply_symmetries_to_curves, apply_symmetries_to_currents +from simsopt.geo import curves_to_vtk, create_equally_spaced_curves +from simsopt.geo import CurveLength, CurveCurveDistance, CurveSurfaceDistance +from simsopt.geo import SurfaceRZFourier +from simsopt.geo import ArclengthVariation +from simsopt.objectives import SquaredFlux +from simsopt.objectives import QuadraticPenalty +from strain_optimization_classes import create_multifilament_grid_frenet, strain_opt +from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb +import timeit + +""" +This script performs a stage two coil optimization +with additional terms for torsion and binormal curvature in the cost function. +We use a multifilament approach that initializes the coils using the Frener-Serret Frame +""" + +# Number of unique coil shapes, i.e. the number of coils per half field period: +# (Since the configuration has nfp = 2, multiply by 4 to get the total number of coils.) +ncoils = 4 + +# Major radius for the initial circular coils: +R0 = 1.00 + +# Minor radius for the initial circular coils: +R1 = 0.70 + +# Number of Fourier modes describing each Cartesian component of each coil: +order = 10 + +# Weight on the curve length penalty in the objective function: +LENGTH_PEN = 1e-3 + +# Threshold and weight for the coil-to-coil distance penalty in the objective function: +CC_THRESHOLD = 0.1 +CC_WEIGHT = 1e-4#2e-1 + +# Threshold and weight for the coil-to-surface distance penalty in the objective function: +CS_THRESHOLD = 0.1 +CS_WEIGHT =0#2e-2 + +# weight for penalty on winding pack strain +STRAIN_WEIGHT = 1e-6#3e-7 +# weight for arclength error. +# Arclength parametrization is crucial otherwise the formulas for torsion and curvature are wrong +ARCLENGTH_WEIGHT = 1 + +# Set up the winding pack +numfilaments_n = 1 # number of filaments in normal direction +numfilaments_b = 1 # number of filaments in bi-normal direction +gapsize_n = 0.02 # gap between filaments in normal direction +gapsize_b = 0.03 # gap between filaments in bi-normal direction +rot_order = 10 # order of the Fourier expression for the rotation of the filament pack, i.e. maximum Fourier mode number + +scale = 0.05 +width = 3 +nquad = 50 +# Number of iterations to perform: +ci = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] +MAXITER = 50 if ci else 50 + +####################################################### +# End of input parameters. +####################################################### + +TEST_DIR = (Path(__file__).parent / "/PATH_TO_VMEC_FILE").resolve() +filename = "input.QSErrorVmecFile" #TEST_DIR / 'WOUT_FILE' +# Directory for output +OUT_DIR = "./paul_WIP/output/SN3/" +os.makedirs(OUT_DIR, exist_ok=True) + +config_str = f"{ncoils}_coils_rot_order_{rot_order}_nfn_{numfilaments_n}_nfb_{numfilaments_b}_strain_weight_{STRAIN_WEIGHT}" + +# Initialize the boundary magnetic surface: +nphi = 64 +ntheta = 64 +s = SurfaceRZFourier.from_vmec_input(filename, range="half period", nphi=nphi, ntheta=ntheta) + +nfil = numfilaments_n * numfilaments_b +base_curves = create_equally_spaced_curves(ncoils, s.nfp, stellsym=True, R0=R0, R1=R1, order=order, numquadpoints=nquad) +base_currents = [] +for i in range(ncoils): + curr = Current(1.) + # since the target field is zero, one possible solution is just to set all + # currents to 0. to avoid the minimizer finding that solution, we fix one + # of the currents + if i == 0: + curr.fix_all() + base_currents.append(curr * (5e4/nfil)) + +# use sum here to concatenate lists +base_curves_finite_build = sum([ + create_multifilament_grid_frenet(c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, rotation_order=rot_order) for c in base_curves], []) +base_currents_finite_build = sum([[c]*nfil for c in base_currents], []) + +# apply stellarator and rotation symmetries +curves_fb = apply_symmetries_to_curves(base_curves_finite_build, s.nfp, True) +currents_fb = apply_symmetries_to_currents(base_currents_finite_build, s.nfp, True) +# also apply symmetries to the underlying base curves, as we use those in the +# curve-curve distance penalty +curves = apply_symmetries_to_curves(base_curves, s.nfp, True) + +coils_fb = [Coil(c, curr) for (c, curr) in zip(curves_fb, currents_fb)] +coils_exp = [Coil(c, curr) for (c, curr) in zip(base_curves_finite_build, base_currents_finite_build)] +bs = BiotSavart(coils_fb) +bs.set_points(s.gamma().reshape((-1, 3))) +curves_to_vtk(curves, OUT_DIR + "curves_init") +curves_to_vtk(curves_fb, OUT_DIR + f"curves_init_fb_{config_str}") + +pointData = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} +s.to_vtk(OUT_DIR + f"surf_init_fb_{config_str}", extra_data=pointData) + +# Define the individual terms of the objective function: +Jf = SquaredFlux(s, bs) +Jls = [CurveLength(c) for c in base_curves] +Jccdist = CurveCurveDistance(curves, CC_THRESHOLD, num_basecurves=ncoils) +Jcsdist = CurveSurfaceDistance(curves, s, CS_THRESHOLD) +Jarc = [ArclengthVariation(c) for c in base_curves] +Jstrain_list = [strain_opt(c, width=width, scale=scale) for c in base_curves_finite_build] +Jstrain = sum(QuadraticPenalty(Jstrain_list[i], Jstrain_list[i].J()) for i in range(len(base_curves_finite_build))) + + +# Assemble the objective function +JF = Jf \ + + STRAIN_WEIGHT * Jstrain \ + + LENGTH_PEN * sum(QuadraticPenalty(Jls[i], Jls[i].J()) for i in range(len(base_curves))) \ + + CC_WEIGHT * Jccdist \ + + CS_WEIGHT * Jcsdist \ + + ARCLENGTH_WEIGHT * sum(Jarc) \ + +def fun(dofs): + JF.x = dofs + J = JF.J() + grad = JF.dJ() + #cl_string = ", ".join([f"{J.J():.3f}" for J in Jls]) + #mean_AbsB = np.mean(bs.AbsB()) + #jf = Jf.J() + #kap_string = ", ".join(f"{np.max(c.kappa()):.1f}" for c in base_curves) + #strain_string = Jstrain.J() + #print(f"J={J:.3e}, Jflux={jf:.3e}, sqrt(Jflux)/Mean(|B|)={np.sqrt(jf)/mean_AbsB:.3e}, CoilLengths=[{cl_string}], [{kap_string}], {strain_string:.3e}, ||∇J||={np.linalg.norm(grad):.3e}") + return 1e-4*J, 1e-4*grad + +f = fun +dofs = JF.x + + + +print(""" +################################################################################ +### Run the optimisation ####################################################### +################################################################################ +""") +res = minimize(fun, dofs, jac=True, method='L-BFGS-B', options={'maxiter': MAXITER, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) +export_coils_fb(coils=coils_exp, name=f'coils_opt_{config_str}', path=OUT_DIR, nquad = nquad, nfp=s.nfp, stellsym=True, order=order, numfilaments_n=numfilaments_n, numfilaments_b=numfilaments_b, gapsize_n=gapsize_n, gapsize_b=gapsize_b, rot_order=rot_order) +curves_to_vtk(curves_fb, OUT_DIR + f"curves_opt_fb_{config_str}") +curves_to_vtk(base_curves_finite_build, OUT_DIR + f"curves_opt_fb_hfp_{config_str}") +pointData = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} +s.to_vtk(OUT_DIR + f"surf_opt_fb_{config_str}", extra_data=pointData) From b96555df6b1abf54ed67256007ae56c7d9b9efea Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Tue, 6 Jun 2023 17:49:08 +0200 Subject: [PATCH 05/61] class file for frenet frame finite build --- .../geo/strain_optimization_classes.py | 488 ++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100644 src/simsopt/geo/strain_optimization_classes.py diff --git a/src/simsopt/geo/strain_optimization_classes.py b/src/simsopt/geo/strain_optimization_classes.py new file mode 100644 index 000000000..3b51173d7 --- /dev/null +++ b/src/simsopt/geo/strain_optimization_classes.py @@ -0,0 +1,488 @@ +import numpy as np +import jax.numpy as jnp +from jax import vjp, jvp, grad, debug +from simsopt.geo.jit import jit +from simsopt.geo import ZeroRotation, FilamentRotation, Curve +import simsoptpp as sopp +from simsopt._core import Optimizable +from simsopt._core.derivative import Derivative +from simsopt._core.derivative import derivative_dec +from jax.tree_util import Partial + + +def normal_curvature_pure(n, tdash): + + inner = lambda a, b: np.sum(a*b, axis=1) + normal_curvature = inner( tdash, n ) + return normal_curvature + +def torsion_pure(ndash, b): + inner = lambda a, b: np.sum(a*b, axis=1) + torsion = inner(ndash, b) + return torsion + +torsion2vjp0 = jit(lambda ndash, b, v: vjp(lambda nd: torsion_pure(nd, b), ndash)[1](v)[0]) +torsion2vjp1 = jit(lambda ndash, b, v: vjp(lambda bi: torsion_pure(ndash, bi), b)[1](v)[0]) + + +def binormal_curvature_pure(tdash, b): + inner = lambda a, b: np.sum(a*b, axis=1) + binormal_curvature = inner(tdash, b) + return binormal_curvature + +def anti_twist_pure(n, ndash): + inner = lambda a, b: np.sum(a*b, axis=1) + #func = lambda a: np.arccos(inner(a, np.roll(a, 1, axis=0))) + return inner(n, ndash) * jnp.heaviside(inner(n, ndash), 1) + +def torsion_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width=12, scale=1): + inner = lambda a, b: np.sum(a*b, axis=1) + + gamma = jnp.multiply(gamma, scale) + #gammadash = jnp.multiply(gammadash, scale) + gammadashdash = jnp.multiply(gammadashdash, 1/scale) + gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) + + t, n, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + tdash, ndash, bdash = rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + + curvature = normal_curvature_pure(n, tdash) + torsion = torsion_pure(ndash, b) + binormal_curvature = binormal_curvature_pure(tdash, b) + anti_twist = anti_twist_pure(n, ndash) + w = 0#100 + # 12mm tape limits + t_lim = 12.9 + b_lim = 0.66 + t_lim *= 12 / width + b_lim *= 12 / width + + result = torsion/t_lim + return result + +def binormal_curvature_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width=12, scale=1): + inner = lambda a, b: np.sum(a*b, axis=1) + + gamma = jnp.multiply(gamma, scale) + #gammadash = jnp.multiply(gammadash, scale) + gammadashdash = jnp.multiply(gammadashdash, 1/scale) + gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) + + t, n, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + tdash, ndash, bdash = rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + + curvature = normal_curvature_pure(n, tdash) + torsion = torsion_pure(ndash, b) + binormal_curvature = binormal_curvature_pure(tdash, b) + anti_twist = anti_twist_pure(n, ndash) + w = 0#100 + # 12mm tape limits + t_lim = 12.9 + b_lim = 0.66 + t_lim *= 12 / width + b_lim *= 12 / width + + + result = binormal_curvature/b_lim + return result + +# Optimization function, limits based on 0.2% strain +#@Partial(jit, static_argnums=(6,)) +@jit +def strain_opt_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width=3, scale=1): + tdash1, ndash1, bdash1 = rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + inner = lambda a, b: jnp.sum(a*b, axis=1) + gamma = jnp.multiply(gamma, scale) + #gammadash = jnp.multiply(gammadash, scale) + gammadashdash = jnp.multiply(gammadashdash, 1/scale) + gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) + t, n, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + tdash, ndash, bdash = rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + + curvature = normal_curvature_pure(n, tdash) + torsion = torsion_pure(ndash, b) + binormal_curvature = binormal_curvature_pure(tdash, b) + torsion1=torsion_pure(ndash1, b) + + anti_twist = anti_twist_pure(n, ndash) + w = 0 + # 12mm tape limits + t_lim = 12.9 + b_lim = 0.66 + t_lim *= 12 / width + b_lim *= 12 / width + + result = jnp.sum(jnp.power(torsion/t_lim, 4)) + jnp.sum(jnp.power(binormal_curvature/b_lim, 4)) + w * jnp.sum(anti_twist) + return result + + +class strain_opt(Optimizable): + + def __init__(self, curve, width=3, scale=1): + self.curve = curve + self.width = width + self.scale = scale + #self.J_jax = jit(lambda torsion, binormal_curvature: strain_opt_pure(torsion, binormal_curvature, width)) + self.J_jax = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: strain_opt_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad0 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=0)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad1 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=1)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad2 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=2)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad3 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=3)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad4 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=4)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=5)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.torsion = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: torsion_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: binormal_curvature_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + super().__init__(depends_on=[curve]) + + def torsion_export(self): + + gamma = self.curve.curve.gamma() + d1gamma = self.curve.curve.gammadash() + d2gamma = self.curve.curve.gammadashdash() + d3gamma = self.curve.curve.gammadashdashdash() + alpha = self.curve.rotation.alpha(self.curve.quadpoints) + alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) + scale = self.scale + width = self.width + return self.torsion(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + + def binormal_curvature_export(self): + width = self.width + scale = self.scale + gamma = self.curve.curve.gamma() + d1gamma = self.curve.curve.gammadash() + d2gamma = self.curve.curve.gammadashdash() + d3gamma = self.curve.curve.gammadashdashdash() + alpha = self.curve.rotation.alpha(self.curve.quadpoints) + alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) + + + return self.binormal_curvature(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + + def c_axis_angle(self, B): + gamma = self.curve.curve.gamma() + d1gamma = self.curve.curve.gammadash() + d2gamma = self.curve.curve.gammadashdash() + alpha = self.curve.rotation.alpha(self.curve.quadpoints) + + return self.c_axis(gamma, d1gamma, d2gamma, alpha, B) + + def J(self): + """ + This returns the value of the quantity. + """ + gamma = self.curve.curve.gamma() + d1gamma = self.curve.curve.gammadash() + d2gamma = self.curve.curve.gammadashdash() + d3gamma = self.curve.curve.gammadashdashdash() + alpha = self.curve.rotation.alpha(self.curve.quadpoints) + alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) + width = self.width + scale = self.scale + + return self.J_jax(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + + @derivative_dec + def dJ(self): + """ + This returns the derivative of the quantity with respect to the curve dofs. + """ + gamma = self.curve.curve.gamma() + d1gamma = self.curve.curve.gammadash() + d2gamma = self.curve.curve.gammadashdash() + d3gamma = self.curve.curve.gammadashdashdash() + alpha = self.curve.rotation.alpha(self.curve.quadpoints) + alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) + width = self.width + scale = self.scale + + grad0 = self.thisgrad0(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + grad1 = self.thisgrad1(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + grad2 = self.thisgrad2(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + grad3 = self.thisgrad3(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + grad4 = self.thisgrad4(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + grad5 = self.thisgrad5(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) + + + return self.curve.curve.dgamma_by_dcoeff_vjp(grad0) + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ + + self.curve.curve.dgammadashdash_by_dcoeff_vjp(grad2) + self.curve.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ + + self.curve.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) + self.curve.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + + return_fn_map = {'J': J, 'dJ': dJ} + +@jit +def rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha): + + norm = lambda a: jnp.linalg.norm(a, axis=1) + inner = lambda a, b: jnp.sum(a*b, axis=1) + N = gamma.shape[0] + t, n, b = (np.zeros((N, 3)), np.zeros((N, 3)), np.zeros((N, 3))) + t = gammadash#(1./l[:, None]) * gammadash + t *= 1./jnp.linalg.norm(gammadash, axis=1)[:, None] + + tdash = (1./jnp.linalg.norm(gammadash, axis=1)[:, None])**2 * (jnp.linalg.norm(gammadash, axis=1)[:, None] * gammadashdash + - (inner(gammadash, gammadashdash)/jnp.linalg.norm(gammadash, axis=1))[:, None] * gammadash) + + n = tdash#(1./norm(tdash))[:, None] * tdash + n *= 1/jnp.linalg.norm(tdash, axis=1)[:, None] + b = jnp.cross(t, n, axis=1) + # now rotate the frame by alpha + nn = jnp.cos(alpha)[:, None] * n - jnp.sin(alpha)[:, None] * b + bb = jnp.sin(alpha)[:, None] * n + jnp.cos(alpha)[:, None] * b + + return t, nn, bb + + +rotated_frenet_frame_dash = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: jvp(rotated_frenet_frame, + (gamma, gammadash, gammadashdash, alpha), + (gammadash, gammadashdash, gammadashdashdash, alphadash))[1]) + +rotated_frenet_frame_dcoeff_vjp0 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda g: rotated_frenet_frame(g, gammadash, gammadashdash, alpha), gamma)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp1 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda gd: rotated_frenet_frame(gamma, gd, gammadashdash, alpha), gammadash)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp2 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda gdd: rotated_frenet_frame(gamma, gammadash, gdd, alpha), gammadashdash)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp3 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda a: rotated_frenet_frame(gamma, gammadash, gammadashdash, a), alpha)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp0 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: rotated_frenet_frame_dash(g, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash), gamma)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp1 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gd: rotated_frenet_frame_dash(gamma, gd, gammadashdash, gammadashdashdash, alpha, alphadash), gammadash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp2 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gdd: rotated_frenet_frame_dash(gamma, gammadash, gdd, gammadashdashdash, alpha, alphadash), gammadashdash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp3 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gddd: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gddd, alpha, alphadash), gammadashdashdash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp4 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda a: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, a, alphadash), alpha)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp5 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda ad: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, ad), alphadash)[1](v)[0]) + +def create_multifilament_grid_frenet(curve, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, rotation_order=None, rotation_scaling=None): + """ + Create a regular grid of ``numfilaments_n * numfilaments_b`` many + filaments to approximate a finite-build coil. + + Note that "normal" and "binormal" in the function arguments here + refer not to the Frenet frame but rather to the "coil centroid + frame" defined by Singh et al., before rotation. + + Args: + curve: The underlying curve. + numfilaments_n: number of filaments in normal direction. + numfilaments_b: number of filaments in bi-normal direction. + gapsize_n: gap between filaments in normal direction. + gapsize_b: gap between filaments in bi-normal direction. + rotation_order: Fourier order (maximum mode number) to use in the expression for the rotation + of the filament pack. ``None`` means that the rotation is not optimized. + rotation_scaling: scaling for the rotation degrees of freedom. good + scaling improves the convergence of first order optimization + algorithms. If ``None``, then the default of ``1 / max(gapsize_n, gapsize_b)`` + is used. + """ + if numfilaments_n % 2 == 1: + shifts_n = np.arange(numfilaments_n) - numfilaments_n//2 + else: + shifts_n = np.arange(numfilaments_n) - numfilaments_n/2 + 0.5 + shifts_n = shifts_n * gapsize_n + if numfilaments_b % 2 == 1: + shifts_b = np.arange(numfilaments_b) - numfilaments_b//2 + else: + shifts_b = np.arange(numfilaments_b) - numfilaments_b/2 + 0.5 + shifts_b = shifts_b * gapsize_b + + if rotation_scaling is None: + rotation_scaling = 1/max(gapsize_n, gapsize_b) + if rotation_order is None: + rotation = ZeroRotation(curve.quadpoints) + else: + rotation = FilamentRotation(curve.quadpoints, rotation_order, scale=rotation_scaling) + filaments = [] + for i in range(numfilaments_n): + for j in range(numfilaments_b): + filaments.append(CurveFilament_frenet(curve, shifts_n[i], shifts_b[j], rotation)) + return filaments + + +class CurveFilament_frenet(sopp.Curve, Curve): + + def __init__(self, curve, dn, db, rotation=None): + """ + Implementation as the frenet frame. Given a curve, one defines a normal and + binormal vector and then creates a grid of curves by shifting along the + normal and binormal vector. In addition, we specify an angle along the + curve that allows us to optimise for the rotation of the winding pack. + + + Args: + curve: the underlying curve + dn: how far to move in normal direction + db: how far to move in binormal direction + rotation: angle along the curve to rotate the frame. + """ + self.curve = curve + sopp.Curve.__init__(self, curve.quadpoints) + deps = [curve] + if rotation is not None: + deps.append(rotation) + Curve.__init__(self, depends_on=deps) + self.curve = curve + self.dn = dn + self.db = db + if rotation is None: + rotation = ZeroRotation(curve.quadpoints) + self.rotation = rotation + + # My own stuff + self.t, self.n, self.b = rotated_frenet_frame(curve.gamma(), curve.gammadash(), curve.gammadashdash(), rotation.alpha(curve.quadpoints)) + self.tdash, self.ndash, self.bdash = rotated_frenet_frame_dash(curve.gamma(), curve.gammadash(), curve.gammadashdash(), curve.gammadashdashdash(), rotation.alpha(curve.quadpoints), rotation.alphadash(curve.quadpoints)) + +# self.dnormal_curvature_by_dcoeff_vjp_jax = jit(lambda x, v: vjp(lambda d: torsion_pure(self.ndash, self.b), x)[1](v)[0]) +# self.dbinormal_curvature_by_dcoeff_vjp_jax = jit(lambda x, v: vjp(lambda d: torsion_pure(self.ndash, self.b), x)[1](v)[0]) +# self.dtorsion2_by_dcoeff_vjp_jax = jit(lambda x, v: vjp(lambda d: torsion_pure(self.ndash, self.b), x)[1](v)[0]) + def recompute_bell(self, parent=None): + self.invalidate_cache() + + def gamma_impl(self, gamma, quadpoints): + assert quadpoints.shape[0] == self.curve.quadpoints.shape[0] + assert np.linalg.norm(quadpoints - self.curve.quadpoints) < 1e-15 + c = self.curve + t, n, b = rotated_frenet_frame(c.gamma(), c.gammadash(), c.gammadashdash(), self.rotation.alpha(c.quadpoints)) + gamma[:] = self.curve.gamma() + self.dn * n + self.db * b + + def gammadash_impl(self, gammadash): + c = self.curve + td, nd, bd = rotated_frenet_frame_dash( + c.gamma(), c.gammadash(), c.gammadashdash(), c.gammadashdashdash(), + self.rotation.alpha(c.quadpoints), self.rotation.alphadash(c.quadpoints) + ) + gammadash[:] = self.curve.gammadash() + self.dn * nd + self.db * bd + + def dgamma_by_dcoeff_vjp(self, v): + g = self.curve.gamma() + gd = self.curve.gammadash() + gdd = self.curve.gammadashdash() + a = self.rotation.alpha(self.curve.quadpoints) + zero = np.zeros_like(v) + vg = rotated_frenet_frame_dcoeff_vjp0(g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + vgd = rotated_frenet_frame_dcoeff_vjp1(g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + vgdd = rotated_frenet_frame_dcoeff_vjp2(g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + va = rotated_frenet_frame_dcoeff_vjp3(g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + return self.curve.dgamma_by_dcoeff_vjp(v + vg) \ + + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ + + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) + + def dgammadash_by_dcoeff_vjp(self, v): + g = self.curve.gamma() + gd = self.curve.gammadash() + gdd = self.curve.gammadashdash() + gddd = self.curve.gammadashdashdash() + a = self.rotation.alpha(self.curve.quadpoints) + ad = self.rotation.alphadash(self.curve.quadpoints) + zero = np.zeros_like(v) + + vg = rotated_frenet_frame_dash_dcoeff_vjp0(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgd = rotated_frenet_frame_dash_dcoeff_vjp1(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgdd = rotated_frenet_frame_dash_dcoeff_vjp2(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgddd = rotated_frenet_frame_dash_dcoeff_vjp3(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + va = rotated_frenet_frame_dash_dcoeff_vjp4(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vad = rotated_frenet_frame_dash_dcoeff_vjp5(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + return self.curve.dgamma_by_dcoeff_vjp(vg) \ + + self.curve.dgammadash_by_dcoeff_vjp(v+vgd) \ + + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ + + self.curve.dgammadashdashdash_by_dcoeff_vjp(vgddd) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) \ + + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) + + + + def drotated_frenet_frame_by_dcoeff(self): + r""" + This function returns the derivative of the curve's Frenet frame, + + .. math:: + \left(\frac{\partial \mathbf{t}}{\partial \mathbf{c}}, \frac{\partial \mathbf{n}}{\partial \mathbf{c}}, \frac{\partial \mathbf{b}}{\partial \mathbf{c}}\right), + + with respect to the curve dofs, where :math:`(\mathbf t, \mathbf n, \mathbf b)` correspond to the Frenet frame, and :math:`\mathbf c` are the curve dofs. + """ + c = self.curve + gamma = c.gamma() + dgamma_by_dphi = c.gammadash() + d2gamma_by_dphidphi = c.gammadashdash() + d3gamma_by_dphiphiphi = c.gammadashdashdash() + d2gamma_by_dphidcoeff = c.dgammadash_by_dcoeff() + d3gamma_by_dphidphidcoeff = c.dgammadashdash_by_dcoeff() + d4gamma_bydphiphiphicoeff = c.dgammadashdashdash_by_dcoeff() + + alpha = self.rotation.alpha(c.quadpoints) + + l = c.incremental_arclength() + dl_by_dcoeff = c.dincremental_arclength_by_dcoeff() + + norm = lambda a: np.linalg.norm(a, axis=1) + inner = lambda a, b: np.sum(a*b, axis=1) + inner2 = lambda a, b: np.sum(a*b, axis=2) + outer = lambda a, b: np.einsum('ij, ik -> ijk', a, b) + outer2 = lambda a,b: np.einsum('ijk, ijl -> ijl' , a, b) + N = len(self.quadpoints) + unity = np.repeat(np.identity(3)[None,:], N, axis=0) + + dt_by_dcoeff, dn_by_dcoeff, db_by_dcoeff = (np.zeros((N, 3, c.num_dofs())), np.zeros((N, 3, c.num_dofs())), np.zeros((N, 3, c.num_dofs()))) + t, n, b = rotated_frenet_frame(gamma, dgamma_by_dphi, d2gamma_by_dphidphi, alpha) + dt_by_dcoeff[:, :, :] = -(dl_by_dcoeff[:, None, :]/l[:, None, None]**2) * dgamma_by_dphi[:, :, None] \ + + d2gamma_by_dphidcoeff / l[:, None, None] + + tdash = (1./l[:, None])**2 * ( + l[:, None] * d2gamma_by_dphidphi + - (inner(dgamma_by_dphi, d2gamma_by_dphidphi)/l)[:, None] * dgamma_by_dphi + ) + + dtdash_by_dcoeff = (-2 * dl_by_dcoeff[:, None, :] / l[:, None, None]**3) * (l[:, None] * d2gamma_by_dphidphi - (inner(dgamma_by_dphi, d2gamma_by_dphidphi)/l)[:, None] * dgamma_by_dphi)[:, :, None] \ + + (1./l[:, None, None])**2 * ( + dl_by_dcoeff[:, None, :] * d2gamma_by_dphidphi[:, :, None] + l[:, None, None] * d3gamma_by_dphidphidcoeff + - (inner(d2gamma_by_dphidcoeff, d2gamma_by_dphidphi[:, :, None])[:, None, :]/l[:, None, None]) * dgamma_by_dphi[:, :, None] + - (inner(dgamma_by_dphi[:, :, None], d3gamma_by_dphidphidcoeff)[:, None, :]/l[:, None, None]) * dgamma_by_dphi[:, :, None] + + (inner(dgamma_by_dphi, d2gamma_by_dphidphi)[:, None, None] * dl_by_dcoeff[:, None, :]/l[:, None, None]**2) * dgamma_by_dphi[:, :, None] + - (inner(dgamma_by_dphi, d2gamma_by_dphidphi)/l)[:, None, None] * d2gamma_by_dphidcoeff + ) + dn_by_dcoeff[:, :, :] = (1./norm(tdash))[:, None, None] * dtdash_by_dcoeff \ + - (inner(tdash[:, :, None], dtdash_by_dcoeff)[:, None, :]/inner(tdash, tdash)[:, None, None]**1.5) * tdash[:, :, None] + + + + db_by_dcoeff[:, :, :] = np.cross(dt_by_dcoeff, n[:, :, None], axis=1) + np.cross(t[:, :, None], dn_by_dcoeff, axis=1) + + + + # Implement derivative of the normal vector and its derivative w.r.t. the DOFs of the curve + ndash = d3gamma_by_dphiphiphi / norm(d2gamma_by_dphidphi)[:, None] - ( d2gamma_by_dphidphi * inner(d3gamma_by_dphiphiphi, d2gamma_by_dphidphi)[:, None] ) / norm(d2gamma_by_dphidphi)[:,None]**3 + dndash_by_dcoeff = outer2(((-outer(d3gamma_by_dphiphiphi, d2gamma_by_dphidphi) )/ norm(d2gamma_by_dphidphi)[:,None,None] - 1 / norm(d2gamma_by_dphidphi)[:,None,None] * unity \ + - outer(d2gamma_by_dphidphi, d3gamma_by_dphiphiphi) / norm(d3gamma_by_dphiphiphi)[:,None,None]**3 \ + + 3 * inner(d2gamma_by_dphidphi, d3gamma_by_dphiphiphi)[:,None,None] * outer(d2gamma_by_dphidphi, d3gamma_by_dphiphiphi) / norm(d2gamma_by_dphidphi)[:,None,None]**5), d3gamma_by_dphidphidcoeff ) \ + + outer2((1 / norm(d2gamma_by_dphidphi)[:,None,None] * unity - outer(d2gamma_by_dphidphi, d2gamma_by_dphidphi) / norm(d2gamma_by_dphidphi)[:,None,None]), d4gamma_bydphiphiphicoeff) + + + + + return dt_by_dcoeff, dn_by_dcoeff, db_by_dcoeff, tdash, ndash, dtdash_by_dcoeff, dndash_by_dcoeff From 3e2c0c5138afb1f8e9cd4260f0510661d4f0d90b Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Tue, 6 Jun 2023 18:18:51 +0200 Subject: [PATCH 06/61] make basic example work --- .../3_Advanced/strain_optimization_script.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/3_Advanced/strain_optimization_script.py b/examples/3_Advanced/strain_optimization_script.py index ef57f6ec9..01be38574 100644 --- a/examples/3_Advanced/strain_optimization_script.py +++ b/examples/3_Advanced/strain_optimization_script.py @@ -11,9 +11,8 @@ from simsopt.geo import ArclengthVariation from simsopt.objectives import SquaredFlux from simsopt.objectives import QuadraticPenalty -from strain_optimization_classes import create_multifilament_grid_frenet, strain_opt -from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb -import timeit +from simsopt.geo.strain_optimization_classes import create_multifilament_grid_frenet, strain_opt +#from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb """ This script performs a stage two coil optimization @@ -29,13 +28,13 @@ R0 = 1.00 # Minor radius for the initial circular coils: -R1 = 0.70 +R1 = 0.40 # Number of Fourier modes describing each Cartesian component of each coil: -order = 10 +order = 5 # Weight on the curve length penalty in the objective function: -LENGTH_PEN = 1e-3 +LENGTH_PEN = 1e-2 # Threshold and weight for the coil-to-coil distance penalty in the objective function: CC_THRESHOLD = 0.1 @@ -46,7 +45,7 @@ CS_WEIGHT =0#2e-2 # weight for penalty on winding pack strain -STRAIN_WEIGHT = 1e-6#3e-7 +STRAIN_WEIGHT = 1e-13#3e-7 # weight for arclength error. # Arclength parametrization is crucial otherwise the formulas for torsion and curvature are wrong ARCLENGTH_WEIGHT = 1 @@ -56,23 +55,25 @@ numfilaments_b = 1 # number of filaments in bi-normal direction gapsize_n = 0.02 # gap between filaments in normal direction gapsize_b = 0.03 # gap between filaments in bi-normal direction -rot_order = 10 # order of the Fourier expression for the rotation of the filament pack, i.e. maximum Fourier mode number +rot_order = 5 # order of the Fourier expression for the rotation of the filament pack, i.e. maximum Fourier mode number -scale = 0.05 -width = 3 -nquad = 50 +scale = 1 +width = 12 +#nquad = 50 # Number of iterations to perform: ci = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] -MAXITER = 50 if ci else 50 +MAXITER = 500 if ci else 500 ####################################################### # End of input parameters. ####################################################### -TEST_DIR = (Path(__file__).parent / "/PATH_TO_VMEC_FILE").resolve() -filename = "input.QSErrorVmecFile" #TEST_DIR / 'WOUT_FILE' +# File for the desired boundary magnetic surface: +TEST_DIR = (Path(__file__).parent / ".." / ".." / "tests" / "test_files").resolve() +filename = TEST_DIR / 'input.LandremanPaul2021_QA' + # Directory for output -OUT_DIR = "./paul_WIP/output/SN3/" +OUT_DIR = "./output/" os.makedirs(OUT_DIR, exist_ok=True) config_str = f"{ncoils}_coils_rot_order_{rot_order}_nfn_{numfilaments_n}_nfb_{numfilaments_b}_strain_weight_{STRAIN_WEIGHT}" @@ -83,7 +84,7 @@ s = SurfaceRZFourier.from_vmec_input(filename, range="half period", nphi=nphi, ntheta=ntheta) nfil = numfilaments_n * numfilaments_b -base_curves = create_equally_spaced_curves(ncoils, s.nfp, stellsym=True, R0=R0, R1=R1, order=order, numquadpoints=nquad) +base_curves = create_equally_spaced_curves(ncoils, s.nfp, stellsym=True, R0=R0, R1=R1, order=order) base_currents = [] for i in range(ncoils): curr = Current(1.) @@ -157,8 +158,7 @@ def fun(dofs): ################################################################################ """) res = minimize(fun, dofs, jac=True, method='L-BFGS-B', options={'maxiter': MAXITER, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) -export_coils_fb(coils=coils_exp, name=f'coils_opt_{config_str}', path=OUT_DIR, nquad = nquad, nfp=s.nfp, stellsym=True, order=order, numfilaments_n=numfilaments_n, numfilaments_b=numfilaments_b, gapsize_n=gapsize_n, gapsize_b=gapsize_b, rot_order=rot_order) curves_to_vtk(curves_fb, OUT_DIR + f"curves_opt_fb_{config_str}") -curves_to_vtk(base_curves_finite_build, OUT_DIR + f"curves_opt_fb_hfp_{config_str}") +curves_to_vtk(base_curves_finite_build, OUT_DIR + f"curves_opt_fb_hfp_{config_str}"+"whereisthecoil") pointData = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} s.to_vtk(OUT_DIR + f"surf_opt_fb_{config_str}", extra_data=pointData) From ca39c788cbcf58b4e1c3040320c8df9eaf60f6a0 Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Tue, 13 Jun 2023 11:50:02 +0200 Subject: [PATCH 07/61] cleanups & comments --- .../3_Advanced/strain_optimization_script.py | 99 ++--- .../geo/strain_optimization_classes.py | 346 ++++++++---------- 2 files changed, 214 insertions(+), 231 deletions(-) diff --git a/examples/3_Advanced/strain_optimization_script.py b/examples/3_Advanced/strain_optimization_script.py index 01be38574..28e4fb86d 100644 --- a/examples/3_Advanced/strain_optimization_script.py +++ b/examples/3_Advanced/strain_optimization_script.py @@ -1,7 +1,12 @@ +""" +This script performs a stage two coil optimization +with additional terms for torsion and binormal curvature in the cost function. +We use a multifilament approach that initializes the coils using the Frener-Serret Frame +""" + import os -import numpy as np -import matplotlib.pyplot as plt from pathlib import Path +import numpy as np from scipy.optimize import minimize from simsopt.field import BiotSavart from simsopt.field import Current, Coil, apply_symmetries_to_curves, apply_symmetries_to_currents @@ -11,14 +16,9 @@ from simsopt.geo import ArclengthVariation from simsopt.objectives import SquaredFlux from simsopt.objectives import QuadraticPenalty -from simsopt.geo.strain_optimization_classes import create_multifilament_grid_frenet, strain_opt -#from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb +from simsopt.geo.strain_optimization_classes import create_multifilament_grid_frenet, StrainOpt +# from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb -""" -This script performs a stage two coil optimization -with additional terms for torsion and binormal curvature in the cost function. -We use a multifilament approach that initializes the coils using the Frener-Serret Frame -""" # Number of unique coil shapes, i.e. the number of coils per half field period: # (Since the configuration has nfp = 2, multiply by 4 to get the total number of coils.) @@ -33,33 +33,34 @@ # Number of Fourier modes describing each Cartesian component of each coil: order = 5 +# Set up the winding pack +numfilaments_n = 1 # number of filaments in normal direction +numfilaments_b = 1 # number of filaments in bi-normal direction +gapsize_n = 0.02 # gap between filaments in normal direction +gapsize_b = 0.03 # gap between filaments in bi-normal direction +rot_order = 5 # order of the Fourier expression for the rotation of the filament pack + +scale = 1 +width = 12 + # Weight on the curve length penalty in the objective function: LENGTH_PEN = 1e-2 # Threshold and weight for the coil-to-coil distance penalty in the objective function: CC_THRESHOLD = 0.1 -CC_WEIGHT = 1e-4#2e-1 +CC_WEIGHT = 1e-4 # 2e-1 # Threshold and weight for the coil-to-surface distance penalty in the objective function: CS_THRESHOLD = 0.1 -CS_WEIGHT =0#2e-2 +CS_WEIGHT = 0 # 2e-2 # weight for penalty on winding pack strain -STRAIN_WEIGHT = 1e-13#3e-7 +STRAIN_WEIGHT = 1e-13 # 3e-7 # weight for arclength error. -# Arclength parametrization is crucial otherwise the formulas for torsion and curvature are wrong +# Arclength parametrization is crucial otherwise the formulas for torsion and curvature are wrong ARCLENGTH_WEIGHT = 1 -# Set up the winding pack -numfilaments_n = 1 # number of filaments in normal direction -numfilaments_b = 1 # number of filaments in bi-normal direction -gapsize_n = 0.02 # gap between filaments in normal direction -gapsize_b = 0.03 # gap between filaments in bi-normal direction -rot_order = 5 # order of the Fourier expression for the rotation of the filament pack, i.e. maximum Fourier mode number -scale = 1 -width = 12 -#nquad = 50 # Number of iterations to perform: ci = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] MAXITER = 500 if ci else 500 @@ -69,22 +70,26 @@ ####################################################### # File for the desired boundary magnetic surface: -TEST_DIR = (Path(__file__).parent / ".." / ".." / "tests" / "test_files").resolve() +TEST_DIR = (Path(__file__).parent / ".." / ".." / + "tests" / "test_files").resolve() filename = TEST_DIR / 'input.LandremanPaul2021_QA' # Directory for output OUT_DIR = "./output/" os.makedirs(OUT_DIR, exist_ok=True) -config_str = f"{ncoils}_coils_rot_order_{rot_order}_nfn_{numfilaments_n}_nfb_{numfilaments_b}_strain_weight_{STRAIN_WEIGHT}" +config_str = f"{ncoils}_coils_rot_order_{rot_order}_nfn_{numfilaments_n}_nfb_{numfilaments_b}\ + _strain_weight_{STRAIN_WEIGHT}" # Initialize the boundary magnetic surface: nphi = 64 ntheta = 64 -s = SurfaceRZFourier.from_vmec_input(filename, range="half period", nphi=nphi, ntheta=ntheta) +s = SurfaceRZFourier.from_vmec_input( + filename, range="half period", nphi=nphi, ntheta=ntheta) nfil = numfilaments_n * numfilaments_b -base_curves = create_equally_spaced_curves(ncoils, s.nfp, stellsym=True, R0=R0, R1=R1, order=order) +base_curves = create_equally_spaced_curves( + ncoils, s.nfp, stellsym=True, R0=R0, R1=R1, order=order) base_currents = [] for i in range(ncoils): curr = Current(1.) @@ -97,24 +102,28 @@ # use sum here to concatenate lists base_curves_finite_build = sum([ - create_multifilament_grid_frenet(c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, rotation_order=rot_order) for c in base_curves], []) + create_multifilament_grid_frenet(c, numfilaments_n, numfilaments_b, gapsize_n, + gapsize_b, rotation_order=rot_order) for c in base_curves], []) base_currents_finite_build = sum([[c]*nfil for c in base_currents], []) # apply stellarator and rotation symmetries curves_fb = apply_symmetries_to_curves(base_curves_finite_build, s.nfp, True) -currents_fb = apply_symmetries_to_currents(base_currents_finite_build, s.nfp, True) +currents_fb = apply_symmetries_to_currents( + base_currents_finite_build, s.nfp, True) # also apply symmetries to the underlying base curves, as we use those in the # curve-curve distance penalty curves = apply_symmetries_to_curves(base_curves, s.nfp, True) coils_fb = [Coil(c, curr) for (c, curr) in zip(curves_fb, currents_fb)] -coils_exp = [Coil(c, curr) for (c, curr) in zip(base_curves_finite_build, base_currents_finite_build)] +coils_exp = [Coil(c, curr) for (c, curr) in zip( + base_curves_finite_build, base_currents_finite_build)] bs = BiotSavart(coils_fb) bs.set_points(s.gamma().reshape((-1, 3))) curves_to_vtk(curves, OUT_DIR + "curves_init") curves_to_vtk(curves_fb, OUT_DIR + f"curves_init_fb_{config_str}") -pointData = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} +pointData = {"B_N": np.sum(bs.B().reshape( + (nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} s.to_vtk(OUT_DIR + f"surf_init_fb_{config_str}", extra_data=pointData) # Define the individual terms of the objective function: @@ -123,8 +132,10 @@ Jccdist = CurveCurveDistance(curves, CC_THRESHOLD, num_basecurves=ncoils) Jcsdist = CurveSurfaceDistance(curves, s, CS_THRESHOLD) Jarc = [ArclengthVariation(c) for c in base_curves] -Jstrain_list = [strain_opt(c, width=width, scale=scale) for c in base_curves_finite_build] -Jstrain = sum(QuadraticPenalty(Jstrain_list[i], Jstrain_list[i].J()) for i in range(len(base_curves_finite_build))) +Jstrain_list = [StrainOpt(c, width=width, scale=scale) + for c in base_curves_finite_build] +Jstrain = sum(QuadraticPenalty(Jstrain_list[i], Jstrain_list[i].J( +)) for i in range(len(base_curves_finite_build))) # Assemble the objective function @@ -135,30 +146,34 @@ + CS_WEIGHT * Jcsdist \ + ARCLENGTH_WEIGHT * sum(Jarc) \ + + def fun(dofs): JF.x = dofs J = JF.J() grad = JF.dJ() - #cl_string = ", ".join([f"{J.J():.3f}" for J in Jls]) - #mean_AbsB = np.mean(bs.AbsB()) - #jf = Jf.J() - #kap_string = ", ".join(f"{np.max(c.kappa()):.1f}" for c in base_curves) - #strain_string = Jstrain.J() - #print(f"J={J:.3e}, Jflux={jf:.3e}, sqrt(Jflux)/Mean(|B|)={np.sqrt(jf)/mean_AbsB:.3e}, CoilLengths=[{cl_string}], [{kap_string}], {strain_string:.3e}, ||∇J||={np.linalg.norm(grad):.3e}") + # cl_string = ", ".join([f"{J.J():.3f}" for J in Jls]) + # mean_AbsB = np.mean(bs.AbsB()) + # jf = Jf.J() + # strain_string = Jstrain.J() + # print(f"J={J:.3e}, Jflux={jf:.3e}, sqrt(Jflux)/Mean(|B|)={np.sqrt(jf)/mean_AbsB:.3e}, CoilLengths=[{cl_string}], {strain_string:.3e}, ||∇J||={np.linalg.norm(grad):.3e}") return 1e-4*J, 1e-4*grad + f = fun dofs = JF.x - print(""" ################################################################################ ### Run the optimisation ####################################################### ################################################################################ """) -res = minimize(fun, dofs, jac=True, method='L-BFGS-B', options={'maxiter': MAXITER, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) +res = minimize(fun, dofs, jac=True, method='L-BFGS-B', + options={'maxiter': MAXITER, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) curves_to_vtk(curves_fb, OUT_DIR + f"curves_opt_fb_{config_str}") -curves_to_vtk(base_curves_finite_build, OUT_DIR + f"curves_opt_fb_hfp_{config_str}"+"whereisthecoil") -pointData = {"B_N": np.sum(bs.B().reshape((nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} +curves_to_vtk(base_curves_finite_build, OUT_DIR + + f"curves_opt_fb_hfp_{config_str}"+"whereisthecoil") +pointData = {"B_N": np.sum(bs.B().reshape( + (nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} s.to_vtk(OUT_DIR + f"surf_opt_fb_{config_str}", extra_data=pointData) diff --git a/src/simsopt/geo/strain_optimization_classes.py b/src/simsopt/geo/strain_optimization_classes.py index 3b51173d7..ed1160fc1 100644 --- a/src/simsopt/geo/strain_optimization_classes.py +++ b/src/simsopt/geo/strain_optimization_classes.py @@ -1,108 +1,118 @@ +""" +Implements strain optimization for HTS coils +""" + import numpy as np import jax.numpy as jnp -from jax import vjp, jvp, grad, debug +from jax import vjp, jvp, grad +import simsoptpp as sopp from simsopt.geo.jit import jit from simsopt.geo import ZeroRotation, FilamentRotation, Curve -import simsoptpp as sopp from simsopt._core import Optimizable -from simsopt._core.derivative import Derivative from simsopt._core.derivative import derivative_dec -from jax.tree_util import Partial + + +def inner(a, b): + """Inner product for arrays of shape (N, 3)""" + return np.sum(a*b, axis=1) def normal_curvature_pure(n, tdash): - - inner = lambda a, b: np.sum(a*b, axis=1) - normal_curvature = inner( tdash, n ) + """Implements normal curvature for optimization + (not used in original cost funciton)""" + normal_curvature = inner(tdash, n) return normal_curvature + def torsion_pure(ndash, b): - inner = lambda a, b: np.sum(a*b, axis=1) + """Implements torsion for optimization""" torsion = inner(ndash, b) return torsion -torsion2vjp0 = jit(lambda ndash, b, v: vjp(lambda nd: torsion_pure(nd, b), ndash)[1](v)[0]) -torsion2vjp1 = jit(lambda ndash, b, v: vjp(lambda bi: torsion_pure(ndash, bi), b)[1](v)[0]) + +torsion2vjp0 = jit(lambda ndash, b, v: vjp( + lambda nd: torsion_pure(nd, b), ndash)[1](v)[0]) +torsion2vjp1 = jit(lambda ndash, b, v: vjp( + lambda bi: torsion_pure(ndash, bi), b)[1](v)[0]) def binormal_curvature_pure(tdash, b): - inner = lambda a, b: np.sum(a*b, axis=1) + """Implements binormal currvature for optimization""" binormal_curvature = inner(tdash, b) return binormal_curvature - + + def anti_twist_pure(n, ndash): - inner = lambda a, b: np.sum(a*b, axis=1) - #func = lambda a: np.arccos(inner(a, np.roll(a, 1, axis=0))) + """Function for further penalty on twisted coils""" + # func = lambda a: np.arccos(inner(a, np.roll(a, 1, axis=0))) return inner(n, ndash) * jnp.heaviside(inner(n, ndash), 1) -def torsion_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width=12, scale=1): - inner = lambda a, b: np.sum(a*b, axis=1) - gamma = jnp.multiply(gamma, scale) - #gammadash = jnp.multiply(gammadash, scale) - gammadashdash = jnp.multiply(gammadashdash, 1/scale) - gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) +def torsion_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, + alpha, alphadash, width=12, scale=1): + """Torsion function for export/evaulate coil sets. + Output normalized to 0.2% strain""" - t, n, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) - tdash, ndash, bdash = rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + gamma = jnp.multiply(gamma, scale) + # gammadash = jnp.multiply(gammadash, scale) + gammadashdash = jnp.multiply(gammadashdash, 1/scale) + gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) + + _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + _, ndash, _ = rotated_frenet_frame_dash( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - curvature = normal_curvature_pure(n, tdash) torsion = torsion_pure(ndash, b) - binormal_curvature = binormal_curvature_pure(tdash, b) - anti_twist = anti_twist_pure(n, ndash) - w = 0#100 + # 12mm tape limits t_lim = 12.9 - b_lim = 0.66 t_lim *= 12 / width - b_lim *= 12 / width result = torsion/t_lim return result -def binormal_curvature_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width=12, scale=1): - inner = lambda a, b: np.sum(a*b, axis=1) - gamma = jnp.multiply(gamma, scale) - #gammadash = jnp.multiply(gammadash, scale) - gammadashdash = jnp.multiply(gammadashdash, 1/scale) - gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) +def binormal_curvature_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, + alpha, alphadash, width=12, scale=1): + """Binormal curvature function for export/evaulate coil sets. + Output normalized to 0.2% strain""" - t, n, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) - tdash, ndash, bdash = rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + gamma = jnp.multiply(gamma, scale) + # gammadash = jnp.multiply(gammadash, scale) + gammadashdash = jnp.multiply(gammadashdash, 1/scale) + gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) + + _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + tdash, _, _ = rotated_frenet_frame_dash( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - curvature = normal_curvature_pure(n, tdash) - torsion = torsion_pure(ndash, b) binormal_curvature = binormal_curvature_pure(tdash, b) - anti_twist = anti_twist_pure(n, ndash) - w = 0#100 # 12mm tape limits - t_lim = 12.9 b_lim = 0.66 - t_lim *= 12 / width b_lim *= 12 / width - result = binormal_curvature/b_lim return result # Optimization function, limits based on 0.2% strain -#@Partial(jit, static_argnums=(6,)) +# @Partial(jit, static_argnums=(6,)) + + @jit -def strain_opt_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width=3, scale=1): - tdash1, ndash1, bdash1 = rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - inner = lambda a, b: jnp.sum(a*b, axis=1) - gamma = jnp.multiply(gamma, scale) - #gammadash = jnp.multiply(gammadash, scale) - gammadashdash = jnp.multiply(gammadashdash, 1/scale) - gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) - t, n, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) - tdash, ndash, bdash = rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - - curvature = normal_curvature_pure(n, tdash) +def strain_opt_pure(gamma, gammadash, gammadashdash, gammadashdashdash, + alpha, alphadash, width=3, scale=1): + """Cost function for strain along a HTS coil""" + + gamma = jnp.multiply(gamma, scale) + # gammadash = jnp.multiply(gammadash, scale) + gammadashdash = jnp.multiply(gammadashdash, 1/scale) + gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) + _, n, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + tdash, ndash, _ = rotated_frenet_frame_dash( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + torsion = torsion_pure(ndash, b) binormal_curvature = binormal_curvature_pure(tdash, b) - torsion1=torsion_pure(ndash1, b) anti_twist = anti_twist_pure(n, ndash) w = 0 @@ -112,41 +122,54 @@ def strain_opt_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, a t_lim *= 12 / width b_lim *= 12 / width - result = jnp.sum(jnp.power(torsion/t_lim, 4)) + jnp.sum(jnp.power(binormal_curvature/b_lim, 4)) + w * jnp.sum(anti_twist) + result = jnp.sum(jnp.power(torsion/t_lim, 4)) + \ + jnp.sum(jnp.power(binormal_curvature/b_lim, 4)) + \ + w * jnp.sum(anti_twist) return result -class strain_opt(Optimizable): +class StrainOpt(Optimizable): + """Class for strain optimization""" def __init__(self, curve, width=3, scale=1): self.curve = curve self.width = width self.scale = scale - #self.J_jax = jit(lambda torsion, binormal_curvature: strain_opt_pure(torsion, binormal_curvature, width)) - self.J_jax = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: strain_opt_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad0 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=0)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad1 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=1)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad2 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=2)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad3 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=3)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad4 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=4)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad(self.J_jax, argnums=5)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.torsion = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: torsion_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: binormal_curvature_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + # self.J_jax = jit(lambda torsion, binormal_curvature: strain_opt_pure(torsion, binormal_curvature, width)) + self.J_jax = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: strain_opt_pure( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad0 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( + self.J_jax, argnums=0)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad1 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( + self.J_jax, argnums=1)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad2 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( + self.J_jax, argnums=2)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad3 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( + self.J_jax, argnums=3)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad4 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( + self.J_jax, argnums=4)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.thisgrad5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( + self.J_jax, argnums=5)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.torsion = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: torsion_export_pure( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) + self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: binormal_curvature_export_pure( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) super().__init__(depends_on=[curve]) def torsion_export(self): - - gamma = self.curve.curve.gamma() - d1gamma = self.curve.curve.gammadash() - d2gamma = self.curve.curve.gammadashdash() - d3gamma = self.curve.curve.gammadashdashdash() + """Exports torsion along a coil for a StrainOpt object""" + gamma = self.curve.curve.gamma() + d1gamma = self.curve.curve.gammadash() + d2gamma = self.curve.curve.gammadashdash() + d3gamma = self.curve.curve.gammadashdashdash() alpha = self.curve.rotation.alpha(self.curve.quadpoints) alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) scale = self.scale width = self.width return self.torsion(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - + def binormal_curvature_export(self): + """Exports binormal curvature along a coil for a StrainOpt object""" width = self.width scale = self.scale gamma = self.curve.curve.gamma() @@ -155,18 +178,9 @@ def binormal_curvature_export(self): d3gamma = self.curve.curve.gammadashdashdash() alpha = self.curve.rotation.alpha(self.curve.quadpoints) alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - return self.binormal_curvature(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - def c_axis_angle(self, B): - gamma = self.curve.curve.gamma() - d1gamma = self.curve.curve.gammadash() - d2gamma = self.curve.curve.gammadashdash() - alpha = self.curve.rotation.alpha(self.curve.quadpoints) - - return self.c_axis(gamma, d1gamma, d2gamma, alpha, B) - def J(self): """ This returns the value of the quantity. @@ -179,7 +193,7 @@ def J(self): alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) width = self.width scale = self.scale - + return self.J_jax(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) @derivative_dec @@ -196,34 +210,41 @@ def dJ(self): width = self.width scale = self.scale - grad0 = self.thisgrad0(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - grad1 = self.thisgrad1(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - grad2 = self.thisgrad2(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - grad3 = self.thisgrad3(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - grad4 = self.thisgrad4(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - grad5 = self.thisgrad5(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - + grad0 = self.thisgrad0(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, width, scale) + grad1 = self.thisgrad1(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, width, scale) + grad2 = self.thisgrad2(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, width, scale) + grad3 = self.thisgrad3(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, width, scale) + grad4 = self.thisgrad4(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, width, scale) + grad5 = self.thisgrad5(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, width, scale) return self.curve.curve.dgamma_by_dcoeff_vjp(grad0) + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ + self.curve.curve.dgammadashdash_by_dcoeff_vjp(grad2) + self.curve.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ - + self.curve.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) + self.curve.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + + self.curve.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) + \ + self.curve.rotation.dalphadash_by_dcoeff_vjp( + self.curve.quadpoints, grad5) return_fn_map = {'J': J, 'dJ': dJ} + @jit def rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha): + """Frenet frame of a curve rotated by a angle that varies along the coil path""" - norm = lambda a: jnp.linalg.norm(a, axis=1) - inner = lambda a, b: jnp.sum(a*b, axis=1) N = gamma.shape[0] t, n, b = (np.zeros((N, 3)), np.zeros((N, 3)), np.zeros((N, 3))) - t = gammadash#(1./l[:, None]) * gammadash + t = gammadash # (1./l[:, None]) * gammadash t *= 1./jnp.linalg.norm(gammadash, axis=1)[:, None] tdash = (1./jnp.linalg.norm(gammadash, axis=1)[:, None])**2 * (jnp.linalg.norm(gammadash, axis=1)[:, None] * gammadashdash - - (inner(gammadash, gammadashdash)/jnp.linalg.norm(gammadash, axis=1))[:, None] * gammadash) + - (inner(gammadash, gammadashdash)/jnp.linalg.norm(gammadash, axis=1))[:, None] * gammadash) - n = tdash#(1./norm(tdash))[:, None] * tdash + n = tdash # (1./norm(tdash))[:, None] * tdash n *= 1/jnp.linalg.norm(tdash, axis=1)[:, None] b = jnp.cross(t, n, axis=1) # now rotate the frame by alpha @@ -235,8 +256,9 @@ def rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha): rotated_frenet_frame_dash = jit( lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: jvp(rotated_frenet_frame, - (gamma, gammadash, gammadashdash, alpha), - (gammadash, gammadashdash, gammadashdashdash, alphadash))[1]) + (gamma, gammadash, + gammadashdash, alpha), + (gammadash, gammadashdash, gammadashdashdash, alphadash))[1]) rotated_frenet_frame_dcoeff_vjp0 = jit( lambda gamma, gammadash, gammadashdash, alpha, v: vjp( @@ -278,6 +300,7 @@ def rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha): lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( lambda ad: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, ad), alphadash)[1](v)[0]) + def create_multifilament_grid_frenet(curve, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, rotation_order=None, rotation_scaling=None): """ Create a regular grid of ``numfilaments_n * numfilaments_b`` many @@ -316,15 +339,18 @@ def create_multifilament_grid_frenet(curve, numfilaments_n, numfilaments_b, gaps if rotation_order is None: rotation = ZeroRotation(curve.quadpoints) else: - rotation = FilamentRotation(curve.quadpoints, rotation_order, scale=rotation_scaling) + rotation = FilamentRotation( + curve.quadpoints, rotation_order, scale=rotation_scaling) filaments = [] for i in range(numfilaments_n): for j in range(numfilaments_b): - filaments.append(CurveFilament_frenet(curve, shifts_n[i], shifts_b[j], rotation)) + filaments.append(CurveFilamentFrenet( + curve, shifts_n[i], shifts_b[j], rotation)) return filaments -class CurveFilament_frenet(sopp.Curve, Curve): +class CurveFilamentFrenet(sopp.Curve, Curve): + """Similar to "the conventional finite build class but based on teh Frenet-Frame """ def __init__(self, curve, dn, db, rotation=None): """ @@ -354,8 +380,10 @@ def __init__(self, curve, dn, db, rotation=None): self.rotation = rotation # My own stuff - self.t, self.n, self.b = rotated_frenet_frame(curve.gamma(), curve.gammadash(), curve.gammadashdash(), rotation.alpha(curve.quadpoints)) - self.tdash, self.ndash, self.bdash = rotated_frenet_frame_dash(curve.gamma(), curve.gammadash(), curve.gammadashdash(), curve.gammadashdashdash(), rotation.alpha(curve.quadpoints), rotation.alphadash(curve.quadpoints)) + self.t, self.n, self.b = rotated_frenet_frame(curve.gamma(), curve.gammadash( + ), curve.gammadashdash(), rotation.alpha(curve.quadpoints)) + self.tdash, self.ndash, self.bdash = rotated_frenet_frame_dash(curve.gamma(), curve.gammadash(), curve.gammadashdash( + ), curve.gammadashdashdash(), rotation.alpha(curve.quadpoints), rotation.alphadash(curve.quadpoints)) # self.dnormal_curvature_by_dcoeff_vjp_jax = jit(lambda x, v: vjp(lambda d: torsion_pure(self.ndash, self.b), x)[1](v)[0]) # self.dbinormal_curvature_by_dcoeff_vjp_jax = jit(lambda x, v: vjp(lambda d: torsion_pure(self.ndash, self.b), x)[1](v)[0]) @@ -367,14 +395,16 @@ def gamma_impl(self, gamma, quadpoints): assert quadpoints.shape[0] == self.curve.quadpoints.shape[0] assert np.linalg.norm(quadpoints - self.curve.quadpoints) < 1e-15 c = self.curve - t, n, b = rotated_frenet_frame(c.gamma(), c.gammadash(), c.gammadashdash(), self.rotation.alpha(c.quadpoints)) + _, n, b = rotated_frenet_frame(c.gamma(), c.gammadash( + ), c.gammadashdash(), self.rotation.alpha(c.quadpoints)) gamma[:] = self.curve.gamma() + self.dn * n + self.db * b def gammadash_impl(self, gammadash): c = self.curve - td, nd, bd = rotated_frenet_frame_dash( + _, nd, bd = rotated_frenet_frame_dash( c.gamma(), c.gammadash(), c.gammadashdash(), c.gammadashdashdash(), - self.rotation.alpha(c.quadpoints), self.rotation.alphadash(c.quadpoints) + self.rotation.alpha( + c.quadpoints), self.rotation.alphadash(c.quadpoints) ) gammadash[:] = self.curve.gammadash() + self.dn * nd + self.db * bd @@ -384,10 +414,14 @@ def dgamma_by_dcoeff_vjp(self, v): gdd = self.curve.gammadashdash() a = self.rotation.alpha(self.curve.quadpoints) zero = np.zeros_like(v) - vg = rotated_frenet_frame_dcoeff_vjp0(g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - vgd = rotated_frenet_frame_dcoeff_vjp1(g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - vgdd = rotated_frenet_frame_dcoeff_vjp2(g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - va = rotated_frenet_frame_dcoeff_vjp3(g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + vg = rotated_frenet_frame_dcoeff_vjp0( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + vgd = rotated_frenet_frame_dcoeff_vjp1( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + vgdd = rotated_frenet_frame_dcoeff_vjp2( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + va = rotated_frenet_frame_dcoeff_vjp3( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) return self.curve.dgamma_by_dcoeff_vjp(v + vg) \ + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ @@ -402,87 +436,21 @@ def dgammadash_by_dcoeff_vjp(self, v): ad = self.rotation.alphadash(self.curve.quadpoints) zero = np.zeros_like(v) - vg = rotated_frenet_frame_dash_dcoeff_vjp0(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgd = rotated_frenet_frame_dash_dcoeff_vjp1(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgdd = rotated_frenet_frame_dash_dcoeff_vjp2(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgddd = rotated_frenet_frame_dash_dcoeff_vjp3(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - va = rotated_frenet_frame_dash_dcoeff_vjp4(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vad = rotated_frenet_frame_dash_dcoeff_vjp5(g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vg = rotated_frenet_frame_dash_dcoeff_vjp0( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgd = rotated_frenet_frame_dash_dcoeff_vjp1( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgdd = rotated_frenet_frame_dash_dcoeff_vjp2( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgddd = rotated_frenet_frame_dash_dcoeff_vjp3( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + va = rotated_frenet_frame_dash_dcoeff_vjp4( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vad = rotated_frenet_frame_dash_dcoeff_vjp5( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) return self.curve.dgamma_by_dcoeff_vjp(vg) \ + self.curve.dgammadash_by_dcoeff_vjp(v+vgd) \ + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ + self.curve.dgammadashdashdash_by_dcoeff_vjp(vgddd) \ + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) \ + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) - - - - def drotated_frenet_frame_by_dcoeff(self): - r""" - This function returns the derivative of the curve's Frenet frame, - - .. math:: - \left(\frac{\partial \mathbf{t}}{\partial \mathbf{c}}, \frac{\partial \mathbf{n}}{\partial \mathbf{c}}, \frac{\partial \mathbf{b}}{\partial \mathbf{c}}\right), - - with respect to the curve dofs, where :math:`(\mathbf t, \mathbf n, \mathbf b)` correspond to the Frenet frame, and :math:`\mathbf c` are the curve dofs. - """ - c = self.curve - gamma = c.gamma() - dgamma_by_dphi = c.gammadash() - d2gamma_by_dphidphi = c.gammadashdash() - d3gamma_by_dphiphiphi = c.gammadashdashdash() - d2gamma_by_dphidcoeff = c.dgammadash_by_dcoeff() - d3gamma_by_dphidphidcoeff = c.dgammadashdash_by_dcoeff() - d4gamma_bydphiphiphicoeff = c.dgammadashdashdash_by_dcoeff() - - alpha = self.rotation.alpha(c.quadpoints) - - l = c.incremental_arclength() - dl_by_dcoeff = c.dincremental_arclength_by_dcoeff() - - norm = lambda a: np.linalg.norm(a, axis=1) - inner = lambda a, b: np.sum(a*b, axis=1) - inner2 = lambda a, b: np.sum(a*b, axis=2) - outer = lambda a, b: np.einsum('ij, ik -> ijk', a, b) - outer2 = lambda a,b: np.einsum('ijk, ijl -> ijl' , a, b) - N = len(self.quadpoints) - unity = np.repeat(np.identity(3)[None,:], N, axis=0) - - dt_by_dcoeff, dn_by_dcoeff, db_by_dcoeff = (np.zeros((N, 3, c.num_dofs())), np.zeros((N, 3, c.num_dofs())), np.zeros((N, 3, c.num_dofs()))) - t, n, b = rotated_frenet_frame(gamma, dgamma_by_dphi, d2gamma_by_dphidphi, alpha) - dt_by_dcoeff[:, :, :] = -(dl_by_dcoeff[:, None, :]/l[:, None, None]**2) * dgamma_by_dphi[:, :, None] \ - + d2gamma_by_dphidcoeff / l[:, None, None] - - tdash = (1./l[:, None])**2 * ( - l[:, None] * d2gamma_by_dphidphi - - (inner(dgamma_by_dphi, d2gamma_by_dphidphi)/l)[:, None] * dgamma_by_dphi - ) - - dtdash_by_dcoeff = (-2 * dl_by_dcoeff[:, None, :] / l[:, None, None]**3) * (l[:, None] * d2gamma_by_dphidphi - (inner(dgamma_by_dphi, d2gamma_by_dphidphi)/l)[:, None] * dgamma_by_dphi)[:, :, None] \ - + (1./l[:, None, None])**2 * ( - dl_by_dcoeff[:, None, :] * d2gamma_by_dphidphi[:, :, None] + l[:, None, None] * d3gamma_by_dphidphidcoeff - - (inner(d2gamma_by_dphidcoeff, d2gamma_by_dphidphi[:, :, None])[:, None, :]/l[:, None, None]) * dgamma_by_dphi[:, :, None] - - (inner(dgamma_by_dphi[:, :, None], d3gamma_by_dphidphidcoeff)[:, None, :]/l[:, None, None]) * dgamma_by_dphi[:, :, None] - + (inner(dgamma_by_dphi, d2gamma_by_dphidphi)[:, None, None] * dl_by_dcoeff[:, None, :]/l[:, None, None]**2) * dgamma_by_dphi[:, :, None] - - (inner(dgamma_by_dphi, d2gamma_by_dphidphi)/l)[:, None, None] * d2gamma_by_dphidcoeff - ) - dn_by_dcoeff[:, :, :] = (1./norm(tdash))[:, None, None] * dtdash_by_dcoeff \ - - (inner(tdash[:, :, None], dtdash_by_dcoeff)[:, None, :]/inner(tdash, tdash)[:, None, None]**1.5) * tdash[:, :, None] - - - - db_by_dcoeff[:, :, :] = np.cross(dt_by_dcoeff, n[:, :, None], axis=1) + np.cross(t[:, :, None], dn_by_dcoeff, axis=1) - - - - # Implement derivative of the normal vector and its derivative w.r.t. the DOFs of the curve - ndash = d3gamma_by_dphiphiphi / norm(d2gamma_by_dphidphi)[:, None] - ( d2gamma_by_dphidphi * inner(d3gamma_by_dphiphiphi, d2gamma_by_dphidphi)[:, None] ) / norm(d2gamma_by_dphidphi)[:,None]**3 - dndash_by_dcoeff = outer2(((-outer(d3gamma_by_dphiphiphi, d2gamma_by_dphidphi) )/ norm(d2gamma_by_dphidphi)[:,None,None] - 1 / norm(d2gamma_by_dphidphi)[:,None,None] * unity \ - - outer(d2gamma_by_dphidphi, d3gamma_by_dphiphiphi) / norm(d3gamma_by_dphiphiphi)[:,None,None]**3 \ - + 3 * inner(d2gamma_by_dphidphi, d3gamma_by_dphiphiphi)[:,None,None] * outer(d2gamma_by_dphidphi, d3gamma_by_dphiphiphi) / norm(d2gamma_by_dphidphi)[:,None,None]**5), d3gamma_by_dphidphidcoeff ) \ - + outer2((1 / norm(d2gamma_by_dphidphi)[:,None,None] * unity - outer(d2gamma_by_dphidphi, d2gamma_by_dphidphi) / norm(d2gamma_by_dphidphi)[:,None,None]), d4gamma_bydphiphiphicoeff) - - - - - return dt_by_dcoeff, dn_by_dcoeff, db_by_dcoeff, tdash, ndash, dtdash_by_dcoeff, dndash_by_dcoeff From 051abb35a44c2df15f85286acdded92747f2afe0 Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Tue, 13 Jun 2023 16:46:36 +0200 Subject: [PATCH 08/61] fix typo --- examples/3_Advanced/strain_optimization_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/3_Advanced/strain_optimization_script.py b/examples/3_Advanced/strain_optimization_script.py index 28e4fb86d..2785c703c 100644 --- a/examples/3_Advanced/strain_optimization_script.py +++ b/examples/3_Advanced/strain_optimization_script.py @@ -173,7 +173,7 @@ def fun(dofs): options={'maxiter': MAXITER, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) curves_to_vtk(curves_fb, OUT_DIR + f"curves_opt_fb_{config_str}") curves_to_vtk(base_curves_finite_build, OUT_DIR + - f"curves_opt_fb_hfp_{config_str}"+"whereisthecoil") + f"curves_opt_fb_hfp_{config_str}") pointData = {"B_N": np.sum(bs.B().reshape( (nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} s.to_vtk(OUT_DIR + f"surf_opt_fb_{config_str}", extra_data=pointData) From 1e748b428b306f68cc35a33e7c4c8cbc9da4b134 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Wed, 2 Aug 2023 09:22:11 -0400 Subject: [PATCH 09/61] Initial refactoring. --- examples/3_Advanced/strain_simple.py | 57 ++ src/simsopt/geo/finitebuild.py | 373 +++++++++++-- .../geo/strain_optimization_classes.py | 518 +++--------------- 3 files changed, 456 insertions(+), 492 deletions(-) create mode 100644 examples/3_Advanced/strain_simple.py diff --git a/examples/3_Advanced/strain_simple.py b/examples/3_Advanced/strain_simple.py new file mode 100644 index 000000000..51b60b84b --- /dev/null +++ b/examples/3_Advanced/strain_simple.py @@ -0,0 +1,57 @@ +""" +This script performs a stage two coil optimization +with additional terms for torsion and binormal curvature in the cost function. +We use a multifilament approach that initializes the coils using the Frener-Serret Frame +""" + +import os +from pathlib import Path +import numpy as np +from scipy.optimize import minimize +from simsopt.field import BiotSavart +from simsopt.field import Current, Coil, apply_symmetries_to_curves, apply_symmetries_to_currents +from simsopt.geo import curves_to_vtk, create_equally_spaced_curves +from simsopt.geo import CurveLength, CurveCurveDistance, CurveSurfaceDistance +from simsopt.geo import SurfaceRZFourier +from simsopt.geo import ArclengthVariation +from simsopt.objectives import SquaredFlux +from simsopt.objectives import QuadraticPenalty +from simsopt.geo.strain_optimization_classes import StrainOpt +from simsopt.geo.finitebuild import create_multifilament_grid +# from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb +from simsopt.configs import get_ncsx_data +import matplotlib.pyplot as plt + +curves, currents, ma = get_ncsx_data() +curve = curves[0] + +# Set up the winding pack +numfilaments_n = 1 # number of filaments in normal direction +numfilaments_b = 1 # number of filaments in bi-normal direction +gapsize_n = 0.02 # gap between filaments in normal direction +gapsize_b = 0.03 # gap between filaments in bi-normal direction +rot_order = 5 # order of the Fourier expression for the rotation of the filament pack + +scale = 1 +width = 12 + +# use sum here to concatenate lists +filaments = create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, + gapsize_b, rotation_order=rot_order, frame='frenet') + +strain = StrainOpt(filaments[0], width=width) + +tor = strain.binormal_curvature_strain() + +plt.figure() +plt.plot(tor) +plt.show() + +# tor = strain.torsional_strain() +tor_frame = filaments[0].frame_torsion() +tor_curve = curve.torsion() + +plt.figure() +plt.plot(tor_frame) +plt.plot(tor_curve) +plt.show() diff --git a/src/simsopt/geo/finitebuild.py b/src/simsopt/geo/finitebuild.py index 739cf3df1..442980a36 100644 --- a/src/simsopt/geo/finitebuild.py +++ b/src/simsopt/geo/finitebuild.py @@ -16,53 +16,6 @@ __all__ = ['create_multifilament_grid', 'CurveFilament', 'FilamentRotation', 'ZeroRotation'] - -def create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, rotation_order=None, rotation_scaling=None): - """ - Create a regular grid of ``numfilaments_n * numfilaments_b`` many - filaments to approximate a finite-build coil. - - Note that "normal" and "binormal" in the function arguments here - refer not to the Frenet frame but rather to the "coil centroid - frame" defined by Singh et al., before rotation. - - Args: - curve: The underlying curve. - numfilaments_n: number of filaments in normal direction. - numfilaments_b: number of filaments in bi-normal direction. - gapsize_n: gap between filaments in normal direction. - gapsize_b: gap between filaments in bi-normal direction. - rotation_order: Fourier order (maximum mode number) to use in the expression for the rotation - of the filament pack. ``None`` means that the rotation is not optimized. - rotation_scaling: scaling for the rotation degrees of freedom. good - scaling improves the convergence of first order optimization - algorithms. If ``None``, then the default of ``1 / max(gapsize_n, gapsize_b)`` - is used. - """ - if numfilaments_n % 2 == 1: - shifts_n = np.arange(numfilaments_n) - numfilaments_n//2 - else: - shifts_n = np.arange(numfilaments_n) - numfilaments_n/2 + 0.5 - shifts_n = shifts_n * gapsize_n - if numfilaments_b % 2 == 1: - shifts_b = np.arange(numfilaments_b) - numfilaments_b//2 - else: - shifts_b = np.arange(numfilaments_b) - numfilaments_b/2 + 0.5 - shifts_b = shifts_b * gapsize_b - - if rotation_scaling is None: - rotation_scaling = 1/max(gapsize_n, gapsize_b) - if rotation_order is None: - rotation = ZeroRotation(curve.quadpoints) - else: - rotation = FilamentRotation(curve.quadpoints, rotation_order, scale=rotation_scaling) - filaments = [] - for i in range(numfilaments_n): - for j in range(numfilaments_b): - filaments.append(CurveFilament(curve, shifts_n[i], shifts_b[j], rotation)) - return filaments - - class CurveFilament(sopp.Curve, Curve): def __init__(self, curve, dn, db, rotation=None): @@ -99,7 +52,7 @@ def __init__(self, curve, dn, db, rotation=None): if rotation is None: rotation = ZeroRotation(curve.quadpoints) self.rotation = rotation - + def recompute_bell(self, parent=None): self.invalidate_cache() @@ -107,28 +60,141 @@ def gamma_impl(self, gamma, quadpoints): assert quadpoints.shape[0] == self.curve.quadpoints.shape[0] assert np.linalg.norm(quadpoints - self.curve.quadpoints) < 1e-15 c = self.curve - t, n, b = rotated_centroid_frame(c.gamma(), c.gammadash(), self.rotation.alpha(c.quadpoints)) + t, n, b = self.rotated_frame(c.gamma(), c.gammadash(), self.rotation.alpha(c.quadpoints)) gamma[:] = self.curve.gamma() + self.dn * n + self.db * b def gammadash_impl(self, gammadash): - c = self.curve - td, nd, bd = rotated_centroid_frame_dash( - c.gamma(), c.gammadash(), c.gammadashdash(), - self.rotation.alpha(c.quadpoints), self.rotation.alphadash(c.quadpoints) - ) + td, nd, bd = self.rotated_frame_dash() gammadash[:] = self.curve.gammadash() + self.dn * nd + self.db * bd + +# class FramedCurve(Optimizable): + +# def __init__(self, curve, rotation=None): +# self.curve = curve +# self.rotation = rotation +# super().__init__(depends_on=[curve, rotation]) + +class CurveFilamentFrenet(CurveFilament): + + def __init__(self, curve, dn, db, rotation=None): + CurveFilament.__init__(self, curve, dn, db, rotation=None) + self.torsion = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: torsion_pure_frenet( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) + self.rotated_frame = rotated_frenet_frame + self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: binormal_curvature_pure_frenet( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) + + def rotated_frame_dash(self): + return rotated_frenet_frame_dash( + self.curve.gamma(), self.curve.gammadash(), self.curve.gammadashdash(), self.curve.gammadashdashdash(), + self.rotation.alpha(self.curve.quadpoints), self.rotation.alphadash(self.curve.quadpoints) + ) + + def frame_torsion(self): + """Exports frame torsion along a curve""" + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + d3gamma = self.curve.gammadashdashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + return self.torsion(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash) + + def frame_binormal_curvature(self): + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + d3gamma = self.curve.gammadashdashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + return self.binormal_curvature(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash) + def dgamma_by_dcoeff_vjp(self, v): g = self.curve.gamma() gd = self.curve.gammadash() + gdd = self.curve.gammadashdash() a = self.rotation.alpha(self.curve.quadpoints) zero = np.zeros_like(v) - vg = rotated_centroid_frame_dcoeff_vjp0(g, gd, a, (zero, self.dn*v, self.db*v)) - vgd = rotated_centroid_frame_dcoeff_vjp1(g, gd, a, (zero, self.dn*v, self.db*v)) - va = rotated_centroid_frame_dcoeff_vjp2(g, gd, a, (zero, self.dn*v, self.db*v)) + vg = rotated_frenet_frame_dcoeff_vjp0( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + vgd = rotated_frenet_frame_dcoeff_vjp1( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + vgdd = rotated_frenet_frame_dcoeff_vjp2( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + va = rotated_frenet_frame_dcoeff_vjp3( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) return self.curve.dgamma_by_dcoeff_vjp(v + vg) \ + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ - + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) + + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) + + def dgammadash_by_dcoeff_vjp(self, v): + g = self.curve.gamma() + gd = self.curve.gammadash() + gdd = self.curve.gammadashdash() + gddd = self.curve.gammadashdashdash() + a = self.rotation.alpha(self.curve.quadpoints) + ad = self.rotation.alphadash(self.curve.quadpoints) + zero = np.zeros_like(v) + + vg = rotated_frenet_frame_dash_dcoeff_vjp0( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgd = rotated_frenet_frame_dash_dcoeff_vjp1( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgdd = rotated_frenet_frame_dash_dcoeff_vjp2( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vgddd = rotated_frenet_frame_dash_dcoeff_vjp3( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + va = rotated_frenet_frame_dash_dcoeff_vjp4( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + vad = rotated_frenet_frame_dash_dcoeff_vjp5( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + return self.curve.dgamma_by_dcoeff_vjp(vg) \ + + self.curve.dgammadash_by_dcoeff_vjp(v+vgd) \ + + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ + + self.curve.dgammadashdashdash_by_dcoeff_vjp(vgddd) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) \ + + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) + + +class CurveFilamentCentroid(CurveFilament): + + def __init__(self, curve, dn, db, rotation=None): + CurveFilament.__init__(self, curve, dn, db, rotation=None) + self.rotated_frame = rotated_centroid_frame + self.dgamma_by_dcoeff_vjp = dgamma_by_dcoeff_vjp_centroid + self.dgammadash_by_dcoeff_vjp = dgammadash_by_dcoeff_vjp_centroid + self.torsion = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: torsion_pure_centroid( + gamma, gammadash, gammadashdash, alpha, alphadash)) + self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( + gamma, gammadash, gammadashdash, alpha, alphadash)) + + def frame_torsion(self): + """Exports frame torsion along a curve""" + gamma = self.curve.curve.gamma() + d1gamma = self.curve.curve.gammadash() + d2gamma = self.curve.curve.gammadashdash() + d3gamma = self.curve.curve.gammadashdashdash() + alpha = self.curve.rotation.alpha(self.curve.quadpoints) + alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) + return self.torsion(gamma, d1gamma, d2gamma, alpha, alphadash) + + def frame_binormal_curvature(self): + gamma = self.curve.curve.gamma() + d1gamma = self.curve.curve.gammadash() + d2gamma = self.curve.curve.gammadashdash() + d3gamma = self.curve.curve.gammadashdashdash() + alpha = self.curve.rotation.alpha(self.curve.quadpoints) + alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) + return self.binormal_curvature(gamma, d1gamma, d2gamma, alpha, alphadash) + + + def rotated_frame_dash(self): + return rotated_centroid_frame_dash( + self.curve.gamma(), self.curve.gammadash() , self.curve.gammadashdashdash(), + self.rotation.alpha(self.curve.quadpoints), self.rotation.alphadash(self.curve.quadpoints) + ) def dgammadash_by_dcoeff_vjp(self, v): g = self.curve.gamma() @@ -149,6 +215,68 @@ def dgammadash_by_dcoeff_vjp(self, v): + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) \ + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) + def dgamma_by_dcoeff_vjp(self, v): + g = self.curve.gamma() + gd = self.curve.gammadash() + a = self.rotation.alpha(self.curve.quadpoints) + zero = np.zeros_like(v) + vg = rotated_centroid_frame_dcoeff_vjp0(g, gd, a, (zero, self.dn*v, self.db*v)) + vgd = rotated_centroid_frame_dcoeff_vjp1(g, gd, a, (zero, self.dn*v, self.db*v)) + va = rotated_centroid_frame_dcoeff_vjp2(g, gd, a, (zero, self.dn*v, self.db*v)) + return self.curve.dgamma_by_dcoeff_vjp(v + vg) \ + + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) + +def create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, + rotation_order=None, rotation_scaling=None, frame='centroid'): + """ + Create a regular grid of ``numfilaments_n * numfilaments_b`` many + filaments to approximate a finite-build coil. + + Note that "normal" and "binormal" in the function arguments here + refer not to the Frenet frame but rather to the "coil centroid + frame" defined by Singh et al., before rotation. + + Args: + curve: The underlying curve. + numfilaments_n: number of filaments in normal direction. + numfilaments_b: number of filaments in bi-normal direction. + gapsize_n: gap between filaments in normal direction. + gapsize_b: gap between filaments in bi-normal direction. + rotation_order: Fourier order (maximum mode number) to use in the expression for the rotation + of the filament pack. ``None`` means that the rotation is not optimized. + rotation_scaling: scaling for the rotation degrees of freedom. good + scaling improves the convergence of first order optimization + algorithms. If ``None``, then the default of ``1 / max(gapsize_n, gapsize_b)`` + is used. + """ + assert frame in ['centroid','frenet'] + if numfilaments_n % 2 == 1: + shifts_n = np.arange(numfilaments_n) - numfilaments_n//2 + else: + shifts_n = np.arange(numfilaments_n) - numfilaments_n/2 + 0.5 + shifts_n = shifts_n * gapsize_n + if numfilaments_b % 2 == 1: + shifts_b = np.arange(numfilaments_b) - numfilaments_b//2 + else: + shifts_b = np.arange(numfilaments_b) - numfilaments_b/2 + 0.5 + shifts_b = shifts_b * gapsize_b + + if rotation_scaling is None: + rotation_scaling = 1/max(gapsize_n, gapsize_b) + if rotation_order is None: + rotation = ZeroRotation(curve.quadpoints) + else: + rotation = FilamentRotation(curve.quadpoints, rotation_order, scale=rotation_scaling) + filaments = [] + for i in range(numfilaments_n): + for j in range(numfilaments_b): + if frame=='frenet': + filaments.append(CurveFilamentFrenet(curve, shifts_n[i], shifts_b[j], rotation)) + else: + filaments.append(CurveFilamentCentroid(curve, shifts_n[i], shifts_b[j], rotation)) + return filaments + class FilamentRotation(Optimizable): @@ -229,7 +357,7 @@ def rotated_centroid_frame(gamma, gammadash, alpha): rotated_centroid_frame_dash = jit( - lambda gamma, gammadash, gammadashdash, alpha, alphadash: jvp(rotated_centroid_frame, + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: jvp(rotated_centroid_frame, (gamma, gammadash, alpha), (gammadash, gammadashdash, alphadash))[1]) @@ -265,6 +393,73 @@ def rotated_centroid_frame(gamma, gammadash, alpha): lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda ad: rotated_centroid_frame_dash(gamma, gammadash, gammadashdash, alpha, ad), alphadash)[1](v)[0]) +@jit +def rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha): + """Frenet frame of a curve rotated by a angle that varies along the coil path""" + + N = gamma.shape[0] + t, n, b = (np.zeros((N, 3)), np.zeros((N, 3)), np.zeros((N, 3))) + t = gammadash + t *= 1./jnp.linalg.norm(gammadash, axis=1)[:, None] + + tdash = (1./jnp.linalg.norm(gammadash, axis=1)[:, None])**2 * (jnp.linalg.norm(gammadash, axis=1)[:, None] * gammadashdash + - (inner(gammadash, gammadashdash)/jnp.linalg.norm(gammadash, axis=1))[:, None] * gammadash) + + n = tdash + n *= 1/jnp.linalg.norm(tdash, axis=1)[:, None] + b = jnp.cross(t, n, axis=1) + # now rotate the frame by alpha + nn = jnp.cos(alpha)[:, None] * n - jnp.sin(alpha)[:, None] * b + bb = jnp.sin(alpha)[:, None] * n + jnp.cos(alpha)[:, None] * b + + return t, nn, bb + +rotated_frenet_frame_dash = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: jvp(rotated_frenet_frame, + (gamma, gammadash, + gammadashdash, alpha), + (gammadash, gammadashdash, gammadashdashdash, alphadash))[1]) + +rotated_frenet_frame_dcoeff_vjp0 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda g: rotated_frenet_frame(g, gammadash, gammadashdash, alpha), gamma)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp1 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda gd: rotated_frenet_frame(gamma, gd, gammadashdash, alpha), gammadash)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp2 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda gdd: rotated_frenet_frame(gamma, gammadash, gdd, alpha), gammadashdash)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp3 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda a: rotated_frenet_frame(gamma, gammadash, gammadashdash, a), alpha)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp0 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: rotated_frenet_frame_dash(g, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash), gamma)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp1 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gd: rotated_frenet_frame_dash(gamma, gd, gammadashdash, gammadashdashdash, alpha, alphadash), gammadash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp2 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gdd: rotated_frenet_frame_dash(gamma, gammadash, gdd, gammadashdashdash, alpha, alphadash), gammadashdash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp3 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gddd: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gddd, alpha, alphadash), gammadashdashdash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp4 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda a: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, a, alphadash), alpha)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp5 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda ad: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, ad), alphadash)[1](v)[0]) + def jaxrotation_pure(dofs, points, order): rotation = jnp.zeros((len(points), )) @@ -298,3 +493,63 @@ def rotationdash_dcoeff(points, order): jac[:, 2*j-1] = +2*np.pi*j*np.cos(2*np.pi*j*points) jac[:, 2*j+0] = -2*np.pi*j*np.sin(2*np.pi*j*points) return jac + +def inner(a, b): + """Inner product for arrays of shape (N, 3)""" + return np.sum(a*b, axis=1) + +torsion2vjp0 = jit(lambda ndash, b, v: vjp( + lambda nd: torsion_pure(nd, b), ndash)[1](v)[0]) +torsion2vjp1 = jit(lambda ndash, b, v: vjp( + lambda bi: torsion_pure(ndash, bi), b)[1](v)[0]) + +def binormal_curvature_pure(tdash, b): + """Implements binormal currvature for optimization""" + binormal_curvature = inner(tdash, b) + return binormal_curvature + +def torsion_pure_frenet(gamma, gammadash, gammadashdash, gammadashdashdash, + alpha, alphadash): + """Torsion function for export/evaulate coil sets""" + + _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + _, ndash, _ = rotated_frenet_frame_dash( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + + ndash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] + return inner(ndash, b) + +def binormal_curvature_pure_frenet(gamma, gammadash, gammadashdash, gammadashdashdash, + alpha, alphadash): + + """Binormal curvature function for export/evaulate coil sets.""" + + _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + tdash, _, _ = rotated_frenet_frame_dash( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + + tdash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] + return inner(tdash, b) + +def torsion_pure_centroid(gamma, gammadash, gammadashdash, + alpha, alphadash): + """Torsion function for export/evaulate coil sets""" + + _, _, b = rotated_centroid_frame(gamma, gammadash, alpha) + _, ndash, _ = rotated_centroid_frame_dash( + gamma, gammadash, gammadashdash, alpha, alphadash) + + ndash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] + return inner(ndash, b) + +def binormal_curvature_pure_centroid(gamma, gammadash, gammadashdash, + alpha, alphadash): + + """Binormal curvature function for export/evaulate coil sets.""" + + _, _, b = rotated_centroid_frame(gamma, gammadash, alpha) + tdash, _, _ = rotated_centroid_frame_dash( + gamma, gammadash, gammadashdash, alpha, alphadash) + + tdash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] + return inner(tdash, b) diff --git a/src/simsopt/geo/strain_optimization_classes.py b/src/simsopt/geo/strain_optimization_classes.py index ed1160fc1..22f5df66c 100644 --- a/src/simsopt/geo/strain_optimization_classes.py +++ b/src/simsopt/geo/strain_optimization_classes.py @@ -10,447 +10,99 @@ from simsopt.geo import ZeroRotation, FilamentRotation, Curve from simsopt._core import Optimizable from simsopt._core.derivative import derivative_dec +from simsopt.geo.curveobjectives import Lp_curvature_pure +# class TorsionalStrainPenalty(Optimizable): -def inner(a, b): - """Inner product for arrays of shape (N, 3)""" - return np.sum(a*b, axis=1) +# def __init__(self, curvefilament, width=3, max_strain): +# self.curvefilament = curvefilament +# self.width = width +# self.max_strain = max_strain +# super().__init__(depends_on=[curvefilament]) +# def J(self): +# return Lp_curvature_pure(self.curvefilament.torsional_strain, +# self.curvefilament.curve.gammadash, p, self.max_strain) -def normal_curvature_pure(n, tdash): - """Implements normal curvature for optimization - (not used in original cost funciton)""" - normal_curvature = inner(tdash, n) - return normal_curvature - - -def torsion_pure(ndash, b): - """Implements torsion for optimization""" - torsion = inner(ndash, b) - return torsion - - -torsion2vjp0 = jit(lambda ndash, b, v: vjp( - lambda nd: torsion_pure(nd, b), ndash)[1](v)[0]) -torsion2vjp1 = jit(lambda ndash, b, v: vjp( - lambda bi: torsion_pure(ndash, bi), b)[1](v)[0]) - - -def binormal_curvature_pure(tdash, b): - """Implements binormal currvature for optimization""" - binormal_curvature = inner(tdash, b) - return binormal_curvature - - -def anti_twist_pure(n, ndash): - """Function for further penalty on twisted coils""" - # func = lambda a: np.arccos(inner(a, np.roll(a, 1, axis=0))) - return inner(n, ndash) * jnp.heaviside(inner(n, ndash), 1) - - -def torsion_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, - alpha, alphadash, width=12, scale=1): - """Torsion function for export/evaulate coil sets. - Output normalized to 0.2% strain""" - - gamma = jnp.multiply(gamma, scale) - # gammadash = jnp.multiply(gammadash, scale) - gammadashdash = jnp.multiply(gammadashdash, 1/scale) - gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) - - _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) - _, ndash, _ = rotated_frenet_frame_dash( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - - torsion = torsion_pure(ndash, b) - - # 12mm tape limits - t_lim = 12.9 - t_lim *= 12 / width - - result = torsion/t_lim - return result - - -def binormal_curvature_export_pure(gamma, gammadash, gammadashdash, gammadashdashdash, - alpha, alphadash, width=12, scale=1): - """Binormal curvature function for export/evaulate coil sets. - Output normalized to 0.2% strain""" - - gamma = jnp.multiply(gamma, scale) - # gammadash = jnp.multiply(gammadash, scale) - gammadashdash = jnp.multiply(gammadashdash, 1/scale) - gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) - - _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) - tdash, _, _ = rotated_frenet_frame_dash( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - - binormal_curvature = binormal_curvature_pure(tdash, b) - # 12mm tape limits - b_lim = 0.66 - b_lim *= 12 / width - - result = binormal_curvature/b_lim - return result - -# Optimization function, limits based on 0.2% strain -# @Partial(jit, static_argnums=(6,)) - - -@jit -def strain_opt_pure(gamma, gammadash, gammadashdash, gammadashdashdash, - alpha, alphadash, width=3, scale=1): - """Cost function for strain along a HTS coil""" - - gamma = jnp.multiply(gamma, scale) - # gammadash = jnp.multiply(gammadash, scale) - gammadashdash = jnp.multiply(gammadashdash, 1/scale) - gammadashdashdash = jnp.multiply(gammadashdashdash, 1/scale**2) - _, n, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) - tdash, ndash, _ = rotated_frenet_frame_dash( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - - torsion = torsion_pure(ndash, b) - binormal_curvature = binormal_curvature_pure(tdash, b) - - anti_twist = anti_twist_pure(n, ndash) - w = 0 - # 12mm tape limits - t_lim = 12.9 - b_lim = 0.66 - t_lim *= 12 / width - b_lim *= 12 / width - - result = jnp.sum(jnp.power(torsion/t_lim, 4)) + \ - jnp.sum(jnp.power(binormal_curvature/b_lim, 4)) + \ - w * jnp.sum(anti_twist) - return result +# @derivative_dec +# def dJ(self): class StrainOpt(Optimizable): """Class for strain optimization""" - def __init__(self, curve, width=3, scale=1): - self.curve = curve + def __init__(self, curvefilament, width=3): + self.curvefilament = curvefilament self.width = width - self.scale = scale - # self.J_jax = jit(lambda torsion, binormal_curvature: strain_opt_pure(torsion, binormal_curvature, width)) - self.J_jax = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: strain_opt_pure( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad0 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( - self.J_jax, argnums=0)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad1 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( - self.J_jax, argnums=1)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad2 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( - self.J_jax, argnums=2)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad3 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( - self.J_jax, argnums=3)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad4 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( - self.J_jax, argnums=4)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.thisgrad5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: grad( - self.J_jax, argnums=5)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.torsion = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: torsion_export_pure( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale: binormal_curvature_export_pure( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width, scale)) - super().__init__(depends_on=[curve]) - - def torsion_export(self): + self.J_jax = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: strain_opt_pure( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) + self.thisgrad0 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( + self.J_jax, argnums=0)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) + self.thisgrad1 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( + self.J_jax, argnums=1)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) + self.thisgrad2 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( + self.J_jax, argnums=2)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) + self.thisgrad3 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( + self.J_jax, argnums=3)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) + self.thisgrad4 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( + self.J_jax, argnums=4)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) + self.thisgrad5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( + self.J_jax, argnums=5)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) + super().__init__(depends_on=[curvefilament]) + + def torsional_strain(self): """Exports torsion along a coil for a StrainOpt object""" - gamma = self.curve.curve.gamma() - d1gamma = self.curve.curve.gammadash() - d2gamma = self.curve.curve.gammadashdash() - d3gamma = self.curve.curve.gammadashdashdash() - alpha = self.curve.rotation.alpha(self.curve.quadpoints) - alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - scale = self.scale - width = self.width - return self.torsion(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - - def binormal_curvature_export(self): - """Exports binormal curvature along a coil for a StrainOpt object""" - width = self.width - scale = self.scale - gamma = self.curve.curve.gamma() - d1gamma = self.curve.curve.gammadash() - d2gamma = self.curve.curve.gammadashdash() - d3gamma = self.curve.curve.gammadashdashdash() - alpha = self.curve.rotation.alpha(self.curve.quadpoints) - alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - - return self.binormal_curvature(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - - def J(self): - """ - This returns the value of the quantity. - """ - gamma = self.curve.curve.gamma() - d1gamma = self.curve.curve.gammadash() - d2gamma = self.curve.curve.gammadashdash() - d3gamma = self.curve.curve.gammadashdashdash() - alpha = self.curve.rotation.alpha(self.curve.quadpoints) - alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - width = self.width - scale = self.scale - - return self.J_jax(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width, scale) - - @derivative_dec - def dJ(self): - """ - This returns the derivative of the quantity with respect to the curve dofs. - """ - gamma = self.curve.curve.gamma() - d1gamma = self.curve.curve.gammadash() - d2gamma = self.curve.curve.gammadashdash() - d3gamma = self.curve.curve.gammadashdashdash() - alpha = self.curve.rotation.alpha(self.curve.quadpoints) - alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - width = self.width - scale = self.scale - - grad0 = self.thisgrad0(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, width, scale) - grad1 = self.thisgrad1(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, width, scale) - grad2 = self.thisgrad2(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, width, scale) - grad3 = self.thisgrad3(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, width, scale) - grad4 = self.thisgrad4(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, width, scale) - grad5 = self.thisgrad5(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, width, scale) - - return self.curve.curve.dgamma_by_dcoeff_vjp(grad0) + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ - + self.curve.curve.dgammadashdash_by_dcoeff_vjp(grad2) + self.curve.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ - + self.curve.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) + \ - self.curve.rotation.dalphadash_by_dcoeff_vjp( - self.curve.quadpoints, grad5) - - return_fn_map = {'J': J, 'dJ': dJ} - - -@jit -def rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha): - """Frenet frame of a curve rotated by a angle that varies along the coil path""" - - N = gamma.shape[0] - t, n, b = (np.zeros((N, 3)), np.zeros((N, 3)), np.zeros((N, 3))) - t = gammadash # (1./l[:, None]) * gammadash - t *= 1./jnp.linalg.norm(gammadash, axis=1)[:, None] - - tdash = (1./jnp.linalg.norm(gammadash, axis=1)[:, None])**2 * (jnp.linalg.norm(gammadash, axis=1)[:, None] * gammadashdash - - (inner(gammadash, gammadashdash)/jnp.linalg.norm(gammadash, axis=1))[:, None] * gammadash) - - n = tdash # (1./norm(tdash))[:, None] * tdash - n *= 1/jnp.linalg.norm(tdash, axis=1)[:, None] - b = jnp.cross(t, n, axis=1) - # now rotate the frame by alpha - nn = jnp.cos(alpha)[:, None] * n - jnp.sin(alpha)[:, None] * b - bb = jnp.sin(alpha)[:, None] * n + jnp.cos(alpha)[:, None] * b - - return t, nn, bb - - -rotated_frenet_frame_dash = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: jvp(rotated_frenet_frame, - (gamma, gammadash, - gammadashdash, alpha), - (gammadash, gammadashdash, gammadashdashdash, alphadash))[1]) - -rotated_frenet_frame_dcoeff_vjp0 = jit( - lambda gamma, gammadash, gammadashdash, alpha, v: vjp( - lambda g: rotated_frenet_frame(g, gammadash, gammadashdash, alpha), gamma)[1](v)[0]) - -rotated_frenet_frame_dcoeff_vjp1 = jit( - lambda gamma, gammadash, gammadashdash, alpha, v: vjp( - lambda gd: rotated_frenet_frame(gamma, gd, gammadashdash, alpha), gammadash)[1](v)[0]) - -rotated_frenet_frame_dcoeff_vjp2 = jit( - lambda gamma, gammadash, gammadashdash, alpha, v: vjp( - lambda gdd: rotated_frenet_frame(gamma, gammadash, gdd, alpha), gammadashdash)[1](v)[0]) - -rotated_frenet_frame_dcoeff_vjp3 = jit( - lambda gamma, gammadash, gammadashdash, alpha, v: vjp( - lambda a: rotated_frenet_frame(gamma, gammadash, gammadashdash, a), alpha)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp0 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda g: rotated_frenet_frame_dash(g, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash), gamma)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp1 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda gd: rotated_frenet_frame_dash(gamma, gd, gammadashdash, gammadashdashdash, alpha, alphadash), gammadash)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp2 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda gdd: rotated_frenet_frame_dash(gamma, gammadash, gdd, gammadashdashdash, alpha, alphadash), gammadashdash)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp3 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda gddd: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gddd, alpha, alphadash), gammadashdashdash)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp4 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda a: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, a, alphadash), alpha)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp5 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda ad: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, ad), alphadash)[1](v)[0]) - - -def create_multifilament_grid_frenet(curve, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, rotation_order=None, rotation_scaling=None): - """ - Create a regular grid of ``numfilaments_n * numfilaments_b`` many - filaments to approximate a finite-build coil. - - Note that "normal" and "binormal" in the function arguments here - refer not to the Frenet frame but rather to the "coil centroid - frame" defined by Singh et al., before rotation. - - Args: - curve: The underlying curve. - numfilaments_n: number of filaments in normal direction. - numfilaments_b: number of filaments in bi-normal direction. - gapsize_n: gap between filaments in normal direction. - gapsize_b: gap between filaments in bi-normal direction. - rotation_order: Fourier order (maximum mode number) to use in the expression for the rotation - of the filament pack. ``None`` means that the rotation is not optimized. - rotation_scaling: scaling for the rotation degrees of freedom. good - scaling improves the convergence of first order optimization - algorithms. If ``None``, then the default of ``1 / max(gapsize_n, gapsize_b)`` - is used. - """ - if numfilaments_n % 2 == 1: - shifts_n = np.arange(numfilaments_n) - numfilaments_n//2 - else: - shifts_n = np.arange(numfilaments_n) - numfilaments_n/2 + 0.5 - shifts_n = shifts_n * gapsize_n - if numfilaments_b % 2 == 1: - shifts_b = np.arange(numfilaments_b) - numfilaments_b//2 - else: - shifts_b = np.arange(numfilaments_b) - numfilaments_b/2 + 0.5 - shifts_b = shifts_b * gapsize_b - - if rotation_scaling is None: - rotation_scaling = 1/max(gapsize_n, gapsize_b) - if rotation_order is None: - rotation = ZeroRotation(curve.quadpoints) - else: - rotation = FilamentRotation( - curve.quadpoints, rotation_order, scale=rotation_scaling) - filaments = [] - for i in range(numfilaments_n): - for j in range(numfilaments_b): - filaments.append(CurveFilamentFrenet( - curve, shifts_n[i], shifts_b[j], rotation)) - return filaments - - -class CurveFilamentFrenet(sopp.Curve, Curve): - """Similar to "the conventional finite build class but based on teh Frenet-Frame """ - - def __init__(self, curve, dn, db, rotation=None): - """ - Implementation as the frenet frame. Given a curve, one defines a normal and - binormal vector and then creates a grid of curves by shifting along the - normal and binormal vector. In addition, we specify an angle along the - curve that allows us to optimise for the rotation of the winding pack. - - - Args: - curve: the underlying curve - dn: how far to move in normal direction - db: how far to move in binormal direction - rotation: angle along the curve to rotate the frame. - """ - self.curve = curve - sopp.Curve.__init__(self, curve.quadpoints) - deps = [curve] - if rotation is not None: - deps.append(rotation) - Curve.__init__(self, depends_on=deps) - self.curve = curve - self.dn = dn - self.db = db - if rotation is None: - rotation = ZeroRotation(curve.quadpoints) - self.rotation = rotation - - # My own stuff - self.t, self.n, self.b = rotated_frenet_frame(curve.gamma(), curve.gammadash( - ), curve.gammadashdash(), rotation.alpha(curve.quadpoints)) - self.tdash, self.ndash, self.bdash = rotated_frenet_frame_dash(curve.gamma(), curve.gammadash(), curve.gammadashdash( - ), curve.gammadashdashdash(), rotation.alpha(curve.quadpoints), rotation.alphadash(curve.quadpoints)) - -# self.dnormal_curvature_by_dcoeff_vjp_jax = jit(lambda x, v: vjp(lambda d: torsion_pure(self.ndash, self.b), x)[1](v)[0]) -# self.dbinormal_curvature_by_dcoeff_vjp_jax = jit(lambda x, v: vjp(lambda d: torsion_pure(self.ndash, self.b), x)[1](v)[0]) -# self.dtorsion2_by_dcoeff_vjp_jax = jit(lambda x, v: vjp(lambda d: torsion_pure(self.ndash, self.b), x)[1](v)[0]) - def recompute_bell(self, parent=None): - self.invalidate_cache() - - def gamma_impl(self, gamma, quadpoints): - assert quadpoints.shape[0] == self.curve.quadpoints.shape[0] - assert np.linalg.norm(quadpoints - self.curve.quadpoints) < 1e-15 - c = self.curve - _, n, b = rotated_frenet_frame(c.gamma(), c.gammadash( - ), c.gammadashdash(), self.rotation.alpha(c.quadpoints)) - gamma[:] = self.curve.gamma() + self.dn * n + self.db * b - - def gammadash_impl(self, gammadash): - c = self.curve - _, nd, bd = rotated_frenet_frame_dash( - c.gamma(), c.gammadash(), c.gammadashdash(), c.gammadashdashdash(), - self.rotation.alpha( - c.quadpoints), self.rotation.alphadash(c.quadpoints) - ) - gammadash[:] = self.curve.gammadash() + self.dn * nd + self.db * bd - - def dgamma_by_dcoeff_vjp(self, v): - g = self.curve.gamma() - gd = self.curve.gammadash() - gdd = self.curve.gammadashdash() - a = self.rotation.alpha(self.curve.quadpoints) - zero = np.zeros_like(v) - vg = rotated_frenet_frame_dcoeff_vjp0( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - vgd = rotated_frenet_frame_dcoeff_vjp1( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - vgdd = rotated_frenet_frame_dcoeff_vjp2( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - va = rotated_frenet_frame_dcoeff_vjp3( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - return self.curve.dgamma_by_dcoeff_vjp(v + vg) \ - + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ - + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ - + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) - - def dgammadash_by_dcoeff_vjp(self, v): - g = self.curve.gamma() - gd = self.curve.gammadash() - gdd = self.curve.gammadashdash() - gddd = self.curve.gammadashdashdash() - a = self.rotation.alpha(self.curve.quadpoints) - ad = self.rotation.alphadash(self.curve.quadpoints) - zero = np.zeros_like(v) - - vg = rotated_frenet_frame_dash_dcoeff_vjp0( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgd = rotated_frenet_frame_dash_dcoeff_vjp1( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgdd = rotated_frenet_frame_dash_dcoeff_vjp2( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgddd = rotated_frenet_frame_dash_dcoeff_vjp3( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - va = rotated_frenet_frame_dash_dcoeff_vjp4( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vad = rotated_frenet_frame_dash_dcoeff_vjp5( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - return self.curve.dgamma_by_dcoeff_vjp(vg) \ - + self.curve.dgammadash_by_dcoeff_vjp(v+vgd) \ - + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ - + self.curve.dgammadashdashdash_by_dcoeff_vjp(vgddd) \ - + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) \ - + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) + torsion = self.curvefilament.frame_torsion() + return torsion**2 * self.width**2 / 12 # From 2020 Paz-Soldan + + def binormal_curvature_strain(self): + binormal_curvature = self.curvefilament.frame_binormal_curvature() + return (self.width/2)*binormal_curvature # From 2020 Paz-Soldan + + # def J(self): + # """ + # This returns the value of the quantity. + # """ + # gamma = self.curve.curve.gamma() + # d1gamma = self.curve.curve.gammadash() + # d2gamma = self.curve.curve.gammadashdash() + # d3gamma = self.curve.curve.gammadashdashdash() + # alpha = self.curve.rotation.alpha(self.curve.quadpoints) + # alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) + # width = self.width + + # return self.J_jax(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width) + + # @derivative_dec + # def dJ(self): + # """ + # This returns the derivative of the quantity with respect to the curve dofs. + # """ + # gamma = self.curve.curve.gamma() + # d1gamma = self.curve.curve.gammadash() + # d2gamma = self.curve.curve.gammadashdash() + # d3gamma = self.curve.curve.gammadashdashdash() + # alpha = self.curve.rotation.alpha(self.curve.quadpoints) + # alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) + # width = self.width + + # grad0 = self.thisgrad0(gamma, d1gamma, d2gamma, + # d3gamma, alpha, alphadash, width) + # grad1 = self.thisgrad1(gamma, d1gamma, d2gamma, + # d3gamma, alpha, alphadash, width) + # grad2 = self.thisgrad2(gamma, d1gamma, d2gamma, + # d3gamma, alpha, alphadash, width) + # grad3 = self.thisgrad3(gamma, d1gamma, d2gamma, + # d3gamma, alpha, alphadash, width) + # grad4 = self.thisgrad4(gamma, d1gamma, d2gamma, + # d3gamma, alpha, alphadash, width) + # grad5 = self.thisgrad5(gamma, d1gamma, d2gamma, + # d3gamma, alpha, alphadash, width) + + # return self.curve.curve.dgamma_by_dcoeff_vjp(grad0) + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ + # + self.curve.curve.dgammadashdash_by_dcoeff_vjp(grad2) + self.curve.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ + # + self.curve.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) + \ + # self.curve.rotation.dalphadash_by_dcoeff_vjp( + # self.curve.quadpoints, grad5) + + # return_fn_map = {'J': J, 'dJ': dJ} From bf70cfa750142f35d9c2db95ddb04cdc672ee376 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Fri, 4 Aug 2023 14:43:58 -0400 Subject: [PATCH 10/61] Cleanup and refactoring. --- examples/3_Advanced/strain_simple.py | 17 +- src/simsopt/field/magneticfieldclasses.py | 4 +- src/simsopt/geo/__init__.py | 2 +- src/simsopt/geo/finitebuild.py | 470 ++---------------- .../geo/strain_optimization_classes.py | 16 +- 5 files changed, 55 insertions(+), 454 deletions(-) diff --git a/examples/3_Advanced/strain_simple.py b/examples/3_Advanced/strain_simple.py index 51b60b84b..f11fac807 100644 --- a/examples/3_Advanced/strain_simple.py +++ b/examples/3_Advanced/strain_simple.py @@ -17,7 +17,7 @@ from simsopt.objectives import SquaredFlux from simsopt.objectives import QuadraticPenalty from simsopt.geo.strain_optimization_classes import StrainOpt -from simsopt.geo.finitebuild import create_multifilament_grid +from simsopt.geo import create_multifilament_grid, ZeroRotation, FramedCurveFrenet, FrameRotation # from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb from simsopt.configs import get_ncsx_data import matplotlib.pyplot as plt @@ -26,20 +26,14 @@ curve = curves[0] # Set up the winding pack -numfilaments_n = 1 # number of filaments in normal direction -numfilaments_b = 1 # number of filaments in bi-normal direction -gapsize_n = 0.02 # gap between filaments in normal direction -gapsize_b = 0.03 # gap between filaments in bi-normal direction rot_order = 5 # order of the Fourier expression for the rotation of the filament pack -scale = 1 width = 12 -# use sum here to concatenate lists -filaments = create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, - gapsize_b, rotation_order=rot_order, frame='frenet') +rotation = FrameRotation(curve.quadpoints,rot_order) +framedcurve = FramedCurveFrenet(curve, rotation) -strain = StrainOpt(filaments[0], width=width) +strain = StrainOpt(framedcurve, width=width) tor = strain.binormal_curvature_strain() @@ -48,8 +42,9 @@ plt.show() # tor = strain.torsional_strain() -tor_frame = filaments[0].frame_torsion() +tor_frame = framedcurve.frame_torsion() tor_curve = curve.torsion() +dl = curve.incremental_arclength() plt.figure() plt.plot(tor_frame) diff --git a/src/simsopt/field/magneticfieldclasses.py b/src/simsopt/field/magneticfieldclasses.py index 78928a162..2de2f0343 100644 --- a/src/simsopt/field/magneticfieldclasses.py +++ b/src/simsopt/field/magneticfieldclasses.py @@ -257,8 +257,8 @@ def __init__(self, phi_str): self.phi_str = phi_str self.phi_parsed = parse_expr(phi_str) R, Z, Phi = sp.symbols('R Z phi') - self.Blambdify = sp.lambdify((R, Z, Phi), [self.phi_parsed.diff(R)+1e-30*Phi*R*Z,\ - self.phi_parsed.diff(Phi)/R+1e-30*Phi*R*Z,\ + self.Blambdify = sp.lambdify((R, Z, Phi), [self.phi_parsed.diff(R)+1e-30*Phi*R*Z, \ + self.phi_parsed.diff(Phi)/R+1e-30*Phi*R*Z, \ self.phi_parsed.diff(Z)+1e-30*Phi*R*Z]) self.dBlambdify_by_dX = sp.lambdify( (R, Z, Phi), diff --git a/src/simsopt/geo/__init__.py b/src/simsopt/geo/__init__.py index bbc47fc74..12eca158b 100644 --- a/src/simsopt/geo/__init__.py +++ b/src/simsopt/geo/__init__.py @@ -8,7 +8,7 @@ from .curvexyzfourier import * from .curveperturbed import * from .curveobjectives import * - +from .framedcurve import * from .finitebuild import * from .plotting import * diff --git a/src/simsopt/geo/finitebuild.py b/src/simsopt/geo/finitebuild.py index 442980a36..8e45f4ac1 100644 --- a/src/simsopt/geo/finitebuild.py +++ b/src/simsopt/geo/finitebuild.py @@ -7,32 +7,29 @@ from .._core.derivative import Derivative from .curve import Curve from .jit import jit +from .framedcurve import FramedCurve """ The functions and classes in this model are used to deal with multifilament approximation of finite build coils. """ -__all__ = ['create_multifilament_grid', - 'CurveFilament', 'FilamentRotation', 'ZeroRotation'] +__all__ = ['create_multifilament_grid','CurveFilament'] -class CurveFilament(sopp.Curve, Curve): - def __init__(self, curve, dn, db, rotation=None): +class CurveFilament(FramedCurve): + + def __init__(self, framedcurve, dn, db): """ - Implementation of the centroid frame introduced in - Singh et al, "Optimization of finite-build stellarator coils", - Journal of Plasma Physics 86 (2020), - doi:10.1017/S0022377820000756. Given a curve, one defines a normal and - binormal vector and then creates a grid of curves by shifting along the - normal and binormal vector. In addition, we specify an angle along the - curve that allows us to optimise for the rotation of the winding pack. + Given a FramedCurve, defining a normal and + binormal vector, create a grid of curves by shifting + along the normal and binormal vector. - The idea is explained well in Figure 1 in the reference above. + The idea is explained well in Figure 1 in the reference: - Note that "normal" and "binormal" in the function arguments here - refer not to the Frenet frame but rather to the "coil centroid - frame" defined by Singh et al., before rotation. + Singh et al, "Optimization of finite-build stellarator coils", + Journal of Plasma Physics 86 (2020), + doi:10.1017/S0022377820000756. Args: curve: the underlying curve @@ -40,19 +37,13 @@ def __init__(self, curve, dn, db, rotation=None): db: how far to move in binormal direction rotation: angle along the curve to rotate the frame. """ - self.curve = curve - sopp.Curve.__init__(self, curve.quadpoints) - deps = [curve] - if rotation is not None: - deps.append(rotation) - Curve.__init__(self, depends_on=deps) - self.curve = curve + self.curve = framedcurve.curve self.dn = dn self.db = db - if rotation is None: - rotation = ZeroRotation(curve.quadpoints) - self.rotation = rotation - + self.rotation = framedcurve.rotation + self.framedcurve = framedcurve + FramedCurve.__init__(self, self.curve, self.rotation) + def recompute_bell(self, parent=None): self.invalidate_cache() @@ -67,62 +58,19 @@ def gammadash_impl(self, gammadash): td, nd, bd = self.rotated_frame_dash() gammadash[:] = self.curve.gammadash() + self.dn * nd + self.db * bd - -# class FramedCurve(Optimizable): - -# def __init__(self, curve, rotation=None): -# self.curve = curve -# self.rotation = rotation -# super().__init__(depends_on=[curve, rotation]) - -class CurveFilamentFrenet(CurveFilament): - - def __init__(self, curve, dn, db, rotation=None): - CurveFilament.__init__(self, curve, dn, db, rotation=None) - self.torsion = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: torsion_pure_frenet( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) - self.rotated_frame = rotated_frenet_frame - self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: binormal_curvature_pure_frenet( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) - - def rotated_frame_dash(self): - return rotated_frenet_frame_dash( - self.curve.gamma(), self.curve.gammadash(), self.curve.gammadashdash(), self.curve.gammadashdashdash(), - self.rotation.alpha(self.curve.quadpoints), self.rotation.alphadash(self.curve.quadpoints) - ) - - def frame_torsion(self): - """Exports frame torsion along a curve""" - gamma = self.curve.gamma() - d1gamma = self.curve.gammadash() - d2gamma = self.curve.gammadashdash() - d3gamma = self.curve.gammadashdashdash() - alpha = self.rotation.alpha(self.curve.quadpoints) - alphadash = self.rotation.alphadash(self.curve.quadpoints) - return self.torsion(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash) - - def frame_binormal_curvature(self): - gamma = self.curve.gamma() - d1gamma = self.curve.gammadash() - d2gamma = self.curve.gammadashdash() - d3gamma = self.curve.gammadashdashdash() - alpha = self.rotation.alpha(self.curve.quadpoints) - alphadash = self.rotation.alphadash(self.curve.quadpoints) - return self.binormal_curvature(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash) - def dgamma_by_dcoeff_vjp(self, v): g = self.curve.gamma() gd = self.curve.gammadash() gdd = self.curve.gammadashdash() a = self.rotation.alpha(self.curve.quadpoints) zero = np.zeros_like(v) - vg = rotated_frenet_frame_dcoeff_vjp0( + vg = self.framedcurve.rotated_frame_dcoeff_vjp0( g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - vgd = rotated_frenet_frame_dcoeff_vjp1( + vgd = self.framedcurve.rotated_frame_dcoeff_vjp1( g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - vgdd = rotated_frenet_frame_dcoeff_vjp2( + vgdd = self.framedcurve.rotated_frame_dcoeff_vjp2( g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - va = rotated_frenet_frame_dcoeff_vjp3( + va = self.framedcurve.rotated_frame_dcoeff_vjp3( g, gd, gdd, a, (zero, self.dn*v, self.db*v)) return self.curve.dgamma_by_dcoeff_vjp(v + vg) \ + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ @@ -138,17 +86,17 @@ def dgammadash_by_dcoeff_vjp(self, v): ad = self.rotation.alphadash(self.curve.quadpoints) zero = np.zeros_like(v) - vg = rotated_frenet_frame_dash_dcoeff_vjp0( + vg = self.framedcurve.rotated_frame_dash_dcoeff_vjp0( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgd = rotated_frenet_frame_dash_dcoeff_vjp1( + vgd = self.framedcurve.rotated_frame_dash_dcoeff_vjp1( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgdd = rotated_frenet_frame_dash_dcoeff_vjp2( + vgdd = self.framedcurve.rotated_frame_dash_dcoeff_vjp2( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgddd = rotated_frenet_frame_dash_dcoeff_vjp3( + vgddd = self.framedcurve.rotated_frame_dash_dcoeff_vjp3( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - va = rotated_frenet_frame_dash_dcoeff_vjp4( + va = self.framedcurve.rotated_frame_dash_dcoeff_vjp4( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vad = rotated_frenet_frame_dash_dcoeff_vjp5( + vad = self.framedcurve.rotated_frame_dash_dcoeff_vjp5( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) return self.curve.dgamma_by_dcoeff_vjp(vg) \ + self.curve.dgammadash_by_dcoeff_vjp(v+vgd) \ @@ -158,83 +106,14 @@ def dgammadash_by_dcoeff_vjp(self, v): + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) -class CurveFilamentCentroid(CurveFilament): - - def __init__(self, curve, dn, db, rotation=None): - CurveFilament.__init__(self, curve, dn, db, rotation=None) - self.rotated_frame = rotated_centroid_frame - self.dgamma_by_dcoeff_vjp = dgamma_by_dcoeff_vjp_centroid - self.dgammadash_by_dcoeff_vjp = dgammadash_by_dcoeff_vjp_centroid - self.torsion = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: torsion_pure_centroid( - gamma, gammadash, gammadashdash, alpha, alphadash)) - self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( - gamma, gammadash, gammadashdash, alpha, alphadash)) - - def frame_torsion(self): - """Exports frame torsion along a curve""" - gamma = self.curve.curve.gamma() - d1gamma = self.curve.curve.gammadash() - d2gamma = self.curve.curve.gammadashdash() - d3gamma = self.curve.curve.gammadashdashdash() - alpha = self.curve.rotation.alpha(self.curve.quadpoints) - alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - return self.torsion(gamma, d1gamma, d2gamma, alpha, alphadash) - - def frame_binormal_curvature(self): - gamma = self.curve.curve.gamma() - d1gamma = self.curve.curve.gammadash() - d2gamma = self.curve.curve.gammadashdash() - d3gamma = self.curve.curve.gammadashdashdash() - alpha = self.curve.rotation.alpha(self.curve.quadpoints) - alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - return self.binormal_curvature(gamma, d1gamma, d2gamma, alpha, alphadash) - - - def rotated_frame_dash(self): - return rotated_centroid_frame_dash( - self.curve.gamma(), self.curve.gammadash() , self.curve.gammadashdashdash(), - self.rotation.alpha(self.curve.quadpoints), self.rotation.alphadash(self.curve.quadpoints) - ) - - def dgammadash_by_dcoeff_vjp(self, v): - g = self.curve.gamma() - gd = self.curve.gammadash() - gdd = self.curve.gammadashdash() - a = self.rotation.alpha(self.curve.quadpoints) - ad = self.rotation.alphadash(self.curve.quadpoints) - zero = np.zeros_like(v) - - vg = rotated_centroid_frame_dash_dcoeff_vjp0(g, gd, gdd, a, ad, (zero, self.dn*v, self.db*v)) - vgd = rotated_centroid_frame_dash_dcoeff_vjp1(g, gd, gdd, a, ad, (zero, self.dn*v, self.db*v)) - vgdd = rotated_centroid_frame_dash_dcoeff_vjp2(g, gd, gdd, a, ad, (zero, self.dn*v, self.db*v)) - va = rotated_centroid_frame_dash_dcoeff_vjp3(g, gd, gdd, a, ad, (zero, self.dn*v, self.db*v)) - vad = rotated_centroid_frame_dash_dcoeff_vjp4(g, gd, gdd, a, ad, (zero, self.dn*v, self.db*v)) - return self.curve.dgamma_by_dcoeff_vjp(vg) \ - + self.curve.dgammadash_by_dcoeff_vjp(v+vgd) \ - + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ - + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) \ - + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) - - def dgamma_by_dcoeff_vjp(self, v): - g = self.curve.gamma() - gd = self.curve.gammadash() - a = self.rotation.alpha(self.curve.quadpoints) - zero = np.zeros_like(v) - vg = rotated_centroid_frame_dcoeff_vjp0(g, gd, a, (zero, self.dn*v, self.db*v)) - vgd = rotated_centroid_frame_dcoeff_vjp1(g, gd, a, (zero, self.dn*v, self.db*v)) - va = rotated_centroid_frame_dcoeff_vjp2(g, gd, a, (zero, self.dn*v, self.db*v)) - return self.curve.dgamma_by_dcoeff_vjp(v + vg) \ - + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ - + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) - def create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, - rotation_order=None, rotation_scaling=None, frame='centroid'): + rotation_order=None, rotation_scaling=None, frame='centroid'): """ Create a regular grid of ``numfilaments_n * numfilaments_b`` many filaments to approximate a finite-build coil. Note that "normal" and "binormal" in the function arguments here - refer not to the Frenet frame but rather to the "coil centroid + refer to either the Frenet frame or the "coil centroid frame" defined by Singh et al., before rotation. Args: @@ -249,8 +128,9 @@ def create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, scaling improves the convergence of first order optimization algorithms. If ``None``, then the default of ``1 / max(gapsize_n, gapsize_b)`` is used. + frame: orthonormal frame to define normal and binormal before rotation (either 'centroid' or 'frenet') """ - assert frame in ['centroid','frenet'] + assert frame in ['centroid', 'frenet'] if numfilaments_n % 2 == 1: shifts_n = np.arange(numfilaments_n) - numfilaments_n//2 else: @@ -267,289 +147,15 @@ def create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, if rotation_order is None: rotation = ZeroRotation(curve.quadpoints) else: - rotation = FilamentRotation(curve.quadpoints, rotation_order, scale=rotation_scaling) + rotation = FrameRotation(curve.quadpoints, rotation_order, scale=rotation_scaling) + if frame == 'frenet': + framedcurve = FramedCurveFrenet(curve, rotation) + else: + framedcurve = FramedCurveCentroid(curve, rotation) + filaments = [] for i in range(numfilaments_n): for j in range(numfilaments_b): - if frame=='frenet': - filaments.append(CurveFilamentFrenet(curve, shifts_n[i], shifts_b[j], rotation)) - else: - filaments.append(CurveFilamentCentroid(curve, shifts_n[i], shifts_b[j], rotation)) + filaments.append(CurveFilament(framedcurve, shifts_n[i], shifts_b[j])) return filaments - -class FilamentRotation(Optimizable): - - def __init__(self, quadpoints, order, scale=1., dofs=None): - """ - The rotation of the multifilament pack; alpha in Figure 1 of - Singh et al, "Optimization of finite-build stellarator coils", - Journal of Plasma Physics 86 (2020), - doi:10.1017/S0022377820000756 - """ - self.order = order - if dofs is None: - super().__init__(x0=np.zeros((2*order+1, ))) - else: - super().__init__(dofs=dofs) - self.quadpoints = quadpoints - self.scale = scale - self.jac = rotation_dcoeff(quadpoints, order) - self.jacdash = rotationdash_dcoeff(quadpoints, order) - self.jax_alpha = jit(lambda dofs, points: jaxrotation_pure(dofs, points, self.order)) - self.jax_alphadash = jit(lambda dofs, points: jaxrotationdash_pure(dofs, points, self.order)) - - def alpha(self, quadpoints): - return self.scale * self.jax_alpha(self._dofs.full_x, quadpoints) - - def alphadash(self, quadpoints): - return self.scale * self.jax_alphadash(self._dofs.full_x, quadpoints) - - def dalpha_by_dcoeff_vjp(self, quadpoints, v): - return Derivative({self: self.scale * sopp.vjp(v, self.jac)}) - - def dalphadash_by_dcoeff_vjp(self, quadpoints, v): - return Derivative({self: self.scale * sopp.vjp(v, self.jacdash)}) - - -class ZeroRotation(Optimizable): - - def __init__(self, quadpoints): - """ - Dummy class that just returns zero for the rotation angle. Equivalent to using - - .. code-block:: python - - rot = FilamentRotation(...) - rot.fix_all() - - """ - super().__init__() - self.zero = np.zeros((quadpoints.size, )) - - def alpha(self, quadpoints): - return self.zero - - def alphadash(self, quadpoints): - return self.zero - - def dalpha_by_dcoeff_vjp(self, quadpoints, v): - return Derivative({}) - - def dalphadash_by_dcoeff_vjp(self, quadpoints, v): - return Derivative({}) - - -@jit -def rotated_centroid_frame(gamma, gammadash, alpha): - t = gammadash - t *= 1./jnp.linalg.norm(gammadash, axis=1)[:, None] - R = jnp.mean(gamma, axis=0) # centroid - delta = gamma - R[None, :] - n = delta - jnp.sum(delta * t, axis=1)[:, None] * t - n *= 1./jnp.linalg.norm(n, axis=1)[:, None] - b = jnp.cross(t, n, axis=1) - - # now rotate the frame by alpha - nn = jnp.cos(alpha)[:, None] * n - jnp.sin(alpha)[:, None] * b - bb = jnp.sin(alpha)[:, None] * n + jnp.cos(alpha)[:, None] * b - return t, nn, bb - - -rotated_centroid_frame_dash = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: jvp(rotated_centroid_frame, - (gamma, gammadash, alpha), - (gammadash, gammadashdash, alphadash))[1]) - -rotated_centroid_frame_dcoeff_vjp0 = jit( - lambda gamma, gammadash, alpha, v: vjp( - lambda g: rotated_centroid_frame(g, gammadash, alpha), gamma)[1](v)[0]) - -rotated_centroid_frame_dcoeff_vjp1 = jit( - lambda gamma, gammadash, alpha, v: vjp( - lambda gd: rotated_centroid_frame(gamma, gd, alpha), gammadash)[1](v)[0]) - -rotated_centroid_frame_dcoeff_vjp2 = jit( - lambda gamma, gammadash, alpha, v: vjp( - lambda a: rotated_centroid_frame(gamma, gammadash, a), alpha)[1](v)[0]) - -rotated_centroid_frame_dash_dcoeff_vjp0 = jit( - lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( - lambda g: rotated_centroid_frame_dash(g, gammadash, gammadashdash, alpha, alphadash), gamma)[1](v)[0]) - -rotated_centroid_frame_dash_dcoeff_vjp1 = jit( - lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( - lambda gd: rotated_centroid_frame_dash(gamma, gd, gammadashdash, alpha, alphadash), gammadash)[1](v)[0]) - -rotated_centroid_frame_dash_dcoeff_vjp2 = jit( - lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( - lambda gdd: rotated_centroid_frame_dash(gamma, gammadash, gdd, alpha, alphadash), gammadashdash)[1](v)[0]) - -rotated_centroid_frame_dash_dcoeff_vjp3 = jit( - lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( - lambda a: rotated_centroid_frame_dash(gamma, gammadash, gammadashdash, a, alphadash), alpha)[1](v)[0]) - -rotated_centroid_frame_dash_dcoeff_vjp4 = jit( - lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( - lambda ad: rotated_centroid_frame_dash(gamma, gammadash, gammadashdash, alpha, ad), alphadash)[1](v)[0]) - -@jit -def rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha): - """Frenet frame of a curve rotated by a angle that varies along the coil path""" - - N = gamma.shape[0] - t, n, b = (np.zeros((N, 3)), np.zeros((N, 3)), np.zeros((N, 3))) - t = gammadash - t *= 1./jnp.linalg.norm(gammadash, axis=1)[:, None] - - tdash = (1./jnp.linalg.norm(gammadash, axis=1)[:, None])**2 * (jnp.linalg.norm(gammadash, axis=1)[:, None] * gammadashdash - - (inner(gammadash, gammadashdash)/jnp.linalg.norm(gammadash, axis=1))[:, None] * gammadash) - - n = tdash - n *= 1/jnp.linalg.norm(tdash, axis=1)[:, None] - b = jnp.cross(t, n, axis=1) - # now rotate the frame by alpha - nn = jnp.cos(alpha)[:, None] * n - jnp.sin(alpha)[:, None] * b - bb = jnp.sin(alpha)[:, None] * n + jnp.cos(alpha)[:, None] * b - - return t, nn, bb - -rotated_frenet_frame_dash = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: jvp(rotated_frenet_frame, - (gamma, gammadash, - gammadashdash, alpha), - (gammadash, gammadashdash, gammadashdashdash, alphadash))[1]) - -rotated_frenet_frame_dcoeff_vjp0 = jit( - lambda gamma, gammadash, gammadashdash, alpha, v: vjp( - lambda g: rotated_frenet_frame(g, gammadash, gammadashdash, alpha), gamma)[1](v)[0]) - -rotated_frenet_frame_dcoeff_vjp1 = jit( - lambda gamma, gammadash, gammadashdash, alpha, v: vjp( - lambda gd: rotated_frenet_frame(gamma, gd, gammadashdash, alpha), gammadash)[1](v)[0]) - -rotated_frenet_frame_dcoeff_vjp2 = jit( - lambda gamma, gammadash, gammadashdash, alpha, v: vjp( - lambda gdd: rotated_frenet_frame(gamma, gammadash, gdd, alpha), gammadashdash)[1](v)[0]) - -rotated_frenet_frame_dcoeff_vjp3 = jit( - lambda gamma, gammadash, gammadashdash, alpha, v: vjp( - lambda a: rotated_frenet_frame(gamma, gammadash, gammadashdash, a), alpha)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp0 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda g: rotated_frenet_frame_dash(g, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash), gamma)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp1 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda gd: rotated_frenet_frame_dash(gamma, gd, gammadashdash, gammadashdashdash, alpha, alphadash), gammadash)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp2 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda gdd: rotated_frenet_frame_dash(gamma, gammadash, gdd, gammadashdashdash, alpha, alphadash), gammadashdash)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp3 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda gddd: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gddd, alpha, alphadash), gammadashdashdash)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp4 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda a: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, a, alphadash), alpha)[1](v)[0]) - -rotated_frenet_frame_dash_dcoeff_vjp5 = jit( - lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( - lambda ad: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, ad), alphadash)[1](v)[0]) - - -def jaxrotation_pure(dofs, points, order): - rotation = jnp.zeros((len(points), )) - rotation += dofs[0] - for j in range(1, order+1): - rotation += dofs[2*j-1] * jnp.sin(2*np.pi*j*points) - rotation += dofs[2*j] * jnp.cos(2*np.pi*j*points) - return rotation - - -def jaxrotationdash_pure(dofs, points, order): - rotation = jnp.zeros((len(points), )) - for j in range(1, order+1): - rotation += dofs[2*j-1] * 2*np.pi*j*jnp.cos(2*np.pi*j*points) - rotation -= dofs[2*j] * 2*np.pi*j*jnp.sin(2*np.pi*j*points) - return rotation - - -def rotation_dcoeff(points, order): - jac = np.zeros((len(points), 2*order+1)) - jac[:, 0] = 1 - for j in range(1, order+1): - jac[:, 2*j-1] = np.sin(2*np.pi*j*points) - jac[:, 2*j+0] = np.cos(2*np.pi*j*points) - return jac - - -def rotationdash_dcoeff(points, order): - jac = np.zeros((len(points), 2*order+1)) - for j in range(1, order+1): - jac[:, 2*j-1] = +2*np.pi*j*np.cos(2*np.pi*j*points) - jac[:, 2*j+0] = -2*np.pi*j*np.sin(2*np.pi*j*points) - return jac - -def inner(a, b): - """Inner product for arrays of shape (N, 3)""" - return np.sum(a*b, axis=1) - -torsion2vjp0 = jit(lambda ndash, b, v: vjp( - lambda nd: torsion_pure(nd, b), ndash)[1](v)[0]) -torsion2vjp1 = jit(lambda ndash, b, v: vjp( - lambda bi: torsion_pure(ndash, bi), b)[1](v)[0]) - -def binormal_curvature_pure(tdash, b): - """Implements binormal currvature for optimization""" - binormal_curvature = inner(tdash, b) - return binormal_curvature - -def torsion_pure_frenet(gamma, gammadash, gammadashdash, gammadashdashdash, - alpha, alphadash): - """Torsion function for export/evaulate coil sets""" - - _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) - _, ndash, _ = rotated_frenet_frame_dash( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - - ndash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] - return inner(ndash, b) - -def binormal_curvature_pure_frenet(gamma, gammadash, gammadashdash, gammadashdashdash, - alpha, alphadash): - - """Binormal curvature function for export/evaulate coil sets.""" - - _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) - tdash, _, _ = rotated_frenet_frame_dash( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) - - tdash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] - return inner(tdash, b) - -def torsion_pure_centroid(gamma, gammadash, gammadashdash, - alpha, alphadash): - """Torsion function for export/evaulate coil sets""" - - _, _, b = rotated_centroid_frame(gamma, gammadash, alpha) - _, ndash, _ = rotated_centroid_frame_dash( - gamma, gammadash, gammadashdash, alpha, alphadash) - - ndash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] - return inner(ndash, b) - -def binormal_curvature_pure_centroid(gamma, gammadash, gammadashdash, - alpha, alphadash): - - """Binormal curvature function for export/evaulate coil sets.""" - - _, _, b = rotated_centroid_frame(gamma, gammadash, alpha) - tdash, _, _ = rotated_centroid_frame_dash( - gamma, gammadash, gammadashdash, alpha, alphadash) - - tdash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] - return inner(tdash, b) diff --git a/src/simsopt/geo/strain_optimization_classes.py b/src/simsopt/geo/strain_optimization_classes.py index 22f5df66c..7ec0dff66 100644 --- a/src/simsopt/geo/strain_optimization_classes.py +++ b/src/simsopt/geo/strain_optimization_classes.py @@ -7,7 +7,7 @@ from jax import vjp, jvp, grad import simsoptpp as sopp from simsopt.geo.jit import jit -from simsopt.geo import ZeroRotation, FilamentRotation, Curve +from simsopt.geo import ZeroRotation, Curve from simsopt._core import Optimizable from simsopt._core.derivative import derivative_dec from simsopt.geo.curveobjectives import Lp_curvature_pure @@ -31,8 +31,8 @@ class StrainOpt(Optimizable): """Class for strain optimization""" - def __init__(self, curvefilament, width=3): - self.curvefilament = curvefilament + def __init__(self, framedcurve, width=3): + self.framedcurve = framedcurve self.width = width self.J_jax = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: strain_opt_pure( gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) @@ -48,16 +48,16 @@ def __init__(self, curvefilament, width=3): self.J_jax, argnums=4)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) self.thisgrad5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( self.J_jax, argnums=5)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) - super().__init__(depends_on=[curvefilament]) + super().__init__(depends_on=[framedcurve]) def torsional_strain(self): """Exports torsion along a coil for a StrainOpt object""" - torsion = self.curvefilament.frame_torsion() - return torsion**2 * self.width**2 / 12 # From 2020 Paz-Soldan + torsion = self.framedcurve.frame_torsion() + return torsion**2 * self.width**2 / 12 # From 2020 Paz-Soldan def binormal_curvature_strain(self): - binormal_curvature = self.curvefilament.frame_binormal_curvature() - return (self.width/2)*binormal_curvature # From 2020 Paz-Soldan + binormal_curvature = self.framedcurve.frame_binormal_curvature() + return (self.width/2)*binormal_curvature # From 2020 Paz-Soldan # def J(self): # """ From d8debdd091c7eca6e3b7a9b55fb8c3ab8196708c Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sun, 6 Aug 2023 14:41:18 -0400 Subject: [PATCH 11/61] Refactoring of strain optimization classes. --- src/simsopt/geo/finitebuild.py | 23 +- src/simsopt/geo/framedcurve.py | 550 ++++++++++++++++++ .../geo/strain_optimization_classes.py | 150 +++-- 3 files changed, 630 insertions(+), 93 deletions(-) create mode 100644 src/simsopt/geo/framedcurve.py diff --git a/src/simsopt/geo/finitebuild.py b/src/simsopt/geo/finitebuild.py index 8e45f4ac1..091d2f364 100644 --- a/src/simsopt/geo/finitebuild.py +++ b/src/simsopt/geo/finitebuild.py @@ -68,14 +68,16 @@ def dgamma_by_dcoeff_vjp(self, v): g, gd, gdd, a, (zero, self.dn*v, self.db*v)) vgd = self.framedcurve.rotated_frame_dcoeff_vjp1( g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - vgdd = self.framedcurve.rotated_frame_dcoeff_vjp2( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) va = self.framedcurve.rotated_frame_dcoeff_vjp3( g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - return self.curve.dgamma_by_dcoeff_vjp(v + vg) \ + out = self.curve.dgamma_by_dcoeff_vjp(v + vg) \ + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ - + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ - + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) + if self.framedcurve.rotated_frame_dcoeff_vjp2 is not None: + vgdd = self.framedcurve.rotated_frame_dcoeff_vjp2( + g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + out += self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) + return out def dgammadash_by_dcoeff_vjp(self, v): g = self.curve.gamma() @@ -92,19 +94,20 @@ def dgammadash_by_dcoeff_vjp(self, v): g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) vgdd = self.framedcurve.rotated_frame_dash_dcoeff_vjp2( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgddd = self.framedcurve.rotated_frame_dash_dcoeff_vjp3( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) va = self.framedcurve.rotated_frame_dash_dcoeff_vjp4( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) vad = self.framedcurve.rotated_frame_dash_dcoeff_vjp5( g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - return self.curve.dgamma_by_dcoeff_vjp(vg) \ + out = self.curve.dgamma_by_dcoeff_vjp(vg) \ + self.curve.dgammadash_by_dcoeff_vjp(v+vgd) \ + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ - + self.curve.dgammadashdashdash_by_dcoeff_vjp(vgddd) \ + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) \ + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) - + if self.framedcurve.rotated_frame_dash_dcoeff_vjp3 is not None: + vgddd = self.framedcurve.rotated_frame_dash_dcoeff_vjp3( + g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + out += self.curve.dgammadashdashdash_by_dcoeff_vjp(vgddd) + return out def create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, rotation_order=None, rotation_scaling=None, frame='centroid'): diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py new file mode 100644 index 000000000..9fe627a58 --- /dev/null +++ b/src/simsopt/geo/framedcurve.py @@ -0,0 +1,550 @@ +import numpy as np +import jax.numpy as jnp +from jax import vjp, jvp, grad + +import simsoptpp as sopp +from .._core.optimizable import Optimizable +from .._core.derivative import Derivative +from .curve import Curve +from .jit import jit + +__all__ = ['FramedCurve', 'FramedCurveFrenet', 'FramedCurveCentroid', + 'FrameRotation', 'ZeroRotation'] + + +class FramedCurve(sopp.Curve, Curve): + + def __init__(self, curve, rotation=None): + """ + A framed curve defines an orthonormal frame around a Curve. + The frame is defined with respect to a reference frame, + either centroid or frenet. A rotation angle defines the rotation + with respect to this reference frame. + """ + self.curve = curve + sopp.Curve.__init__(self, curve.quadpoints) + deps = [curve] + if rotation is not None: + deps.append(rotation) + if rotation is None: + rotation = ZeroRotation(curve.quadpoints) + self.rotation = rotation + Curve.__init__(self, depends_on=deps) + + +class FramedCurveFrenet(FramedCurve): + + """ + Given a curve, one defines a reference frame using the Frenet normal and + binormal vectors: + + tangent = dr/dl + normal = (dtangent/dl)/|dtangent/dl| + binormal = tangent x normal + + In addition, we specify an angle along the curve that + defines the rotation with respect to this reference frame. + """ + + def __init__(self, curve, rotation=None): + FramedCurve.__init__(self, curve, rotation) + + self.binorm = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: binormal_curvature_pure_frenet( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) + self.binormgrad_vjp0 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(g, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash), gamma)[1](v)[0]) + self.binormgrad_vjp1 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, g, gammadashdash, gammadashdashdash, alpha, alphadash), gammadash)[1](v)[0]) + self.binormgrad_vjp2 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, gammadash, g, gammadashdashdash, alpha, alphadash), gammadashdash)[1](v)[0]) + self.binormgrad_vjp3 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, gammadash, gammadashdash, g, alpha, alphadash), gammadashdashdash)[1](v)[0]) + self.binormgrad_vjp4 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, gammadash, gammadashdash, gammadashdashdash, g, alphadash), alpha)[1](v)[0]) + self.binormgrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, g), alphadash)[1](v)[0]) + + self.torsion = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: torsion_pure_frenet( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) + self.torsiongrad_vjp0 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(g, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash), gamma)[1](v)[0]) + self.torsiongrad_vjp1 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, g, gammadashdash, gammadashdashdash, alpha, alphadash), gammadash)[1](v)[0]) + self.torsiongrad_vjp2 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, gammadash, g, gammadashdashdash, alpha, alphadash), gammadashdash)[1](v)[0]) + self.torsiongrad_vjp3 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, gammadash, gammadashdash, g, alpha, alphadash), gammadashdashdash)[1](v)[0]) + self.torsiongrad_vjp4 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, gammadash, gammadashdash, gammadashdashdash, g, alphadash), alpha)[1](v)[0]) + self.torsiongrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, g), alphadash)[1](v)[0]) + + self.rotated_frame = rotated_frenet_frame + self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: binormal_curvature_pure_frenet( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) + self.rotated_frame_dcoeff_vjp0 = rotated_frenet_frame_dcoeff_vjp0 + self.rotated_frame_dcoeff_vjp1 = rotated_frenet_frame_dcoeff_vjp1 + self.rotated_frame_dcoeff_vjp2 = rotated_frenet_frame_dcoeff_vjp2 + self.rotated_frame_dcoeff_vjp3 = rotated_frenet_frame_dcoeff_vjp3 + self.rotated_frame_dash_dcoeff_vjp0 = rotated_frenet_frame_dash_dcoeff_vjp0 + self.rotated_frame_dash_dcoeff_vjp1 = rotated_frenet_frame_dash_dcoeff_vjp1 + self.rotated_frame_dash_dcoeff_vjp2 = rotated_frenet_frame_dash_dcoeff_vjp2 + self.rotated_frame_dash_dcoeff_vjp3 = rotated_frenet_frame_dash_dcoeff_vjp3 + self.rotated_frame_dash_dcoeff_vjp4 = rotated_frenet_frame_dash_dcoeff_vjp4 + self.rotated_frame_dash_dcoeff_vjp5 = rotated_frenet_frame_dash_dcoeff_vjp5 + + def rotated_frame_dash(self): + return rotated_frenet_frame_dash( + self.curve.gamma(), self.curve.gammadash(), self.curve.gammadashdash(), self.curve.gammadashdashdash(), + self.rotation.alpha(self.curve.quadpoints), self.rotation.alphadash(self.curve.quadpoints) + ) + + def frame_torsion(self): + """Exports frame torsion along a curve""" + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + d3gamma = self.curve.gammadashdashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + return self.torsion(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash) + + def frame_binormal_curvature(self): + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + d3gamma = self.curve.gammadashdashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + return self.binormal_curvature(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash) + + def dframe_torsion_by_dcoeff_vjp(self, v): + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + d3gamma = self.curve.gammadashdashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + + grad0 = self.torsiongrad_vjp0(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad1 = self.torsiongrad_vjp1(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad2 = self.torsiongrad_vjp2(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad3 = self.torsiongrad_vjp3(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad4 = self.torsiongrad_vjp4(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad5 = self.torsiongrad_vjp5(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + + return self.curve.dgamma_by_dcoeff_vjp(grad0) \ + + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ + + self.curve.dgammadashdash_by_dcoeff_vjp(grad2) \ + + self.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ + + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + + def dframe_binormal_curvature_by_dcoeff_vjp(self, v): + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + d3gamma = self.curve.gammadashdashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + + grad0 = self.binormgrad_vjp0(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad1 = self.binormgrad_vjp1(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad2 = self.binormgrad_vjp2(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad3 = self.binormgrad_vjp3(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad4 = self.binormgrad_vjp4(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + grad5 = self.binormgrad_vjp5(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash, v) + + return self.curve.dgamma_by_dcoeff_vjp(grad0) \ + + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ + + self.curve.dgammadashdash_by_dcoeff_vjp(grad2) \ + + self.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ + + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + + +class FramedCurveCentroid(FramedCurve): + + """ + Implementation of the centroid frame introduced in + Singh et al, "Optimization of finite-build stellarator coils", + Journal of Plasma Physics 86 (2020), + doi:10.1017/S0022377820000756. + Given a curve, one defines a reference frame using the normal and + binormal vector based on the centoid of the coil. In addition, we specify an + angle along the curve that defines the rotation with respect to this + reference frame. + + The idea is explained well in Figure 1 in the reference above. + """ + + def __init__(self, curve, rotation=None): + FramedCurve.__init__(self, curve, rotation) + self.rotated_frame = rotated_centroid_frame + self.torsion = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: torsion_pure_centroid( + gamma, gammadash, gammadashdash, alpha, alphadash)) + + self.torsiongrad_vjp0 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(g, gammadash, gammadashdash, alpha, alphadash), gamma)[1](v)[0]) + self.torsiongrad_vjp1 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, g, gammadashdash, alpha, alphadash), gammadash)[1](v)[0]) + self.torsiongrad_vjp2 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, gammadash, g, alpha, alphadash), gammadashdash)[1](v)[0]) + self.torsiongrad_vjp3 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, gammadash, gammadashdash, g, alphadash), alpha)[1](v)[0]) + self.torsiongrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.torsion(gamma, gammadash, gammadashdash, alpha, g), alphadash)[1](v)[0]) + + self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( + gamma, gammadash, gammadashdash, alpha, alphadash)) + self.rotated_frame_dcoeff_vjp0 = rotated_centroid_frame_dcoeff_vjp0 + self.rotated_frame_dcoeff_vjp1 = rotated_centroid_frame_dcoeff_vjp1 + self.rotated_frame_dcoeff_vjp2 = None + self.rotated_frame_dcoeff_vjp3 = rotated_centroid_frame_dcoeff_vjp2 + self.rotated_frame_dash_dcoeff_vjp0 = rotated_centroid_frame_dash_dcoeff_vjp0 + self.rotated_frame_dash_dcoeff_vjp1 = rotated_centroid_frame_dash_dcoeff_vjp1 + self.rotated_frame_dash_dcoeff_vjp2 = rotated_centroid_frame_dash_dcoeff_vjp2 + self.rotated_frame_dash_dcoeff_vjp3 = None + self.rotated_frame_dash_dcoeff_vjp4 = rotated_centroid_frame_dash_dcoeff_vjp3 + self.rotated_frame_dash_dcoeff_vjp5 = rotated_centroid_frame_dash_dcoeff_vjp4 + + def frame_torsion(self): + """Exports frame torsion along a curve""" + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + d3gamma = self.curve.gammadashdashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + return self.torsion(gamma, d1gamma, d2gamma, alpha, alphadash) + + def frame_binormal_curvature(self): + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + d3gamma = self.curve.gammadashdashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + return self.binormal_curvature(gamma, d1gamma, d2gamma, alpha, alphadash) + + def rotated_frame_dash(self): + return rotated_centroid_frame_dash( + self.curve.gamma(), self.curve.gammadash(), self.curve.gammadashdash(), + self.rotation.alpha(self.curve.quadpoints), self.rotation.alphadash(self.curve.quadpoints) + ) + + def d_frame_torsion(self): + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + + grad0 = self.torsiongrad0(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash) + grad1 = self.torsiongrad1(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash) + grad2 = self.torsiongrad2(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash) + grad4 = self.torsiongrad4(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash) + grad5 = self.torsiongrad5(gamma, d1gamma, d2gamma, + d3gamma, alpha, alphadash) + + return self.curve.dgamma_by_dcoeff_vjp(grad0) \ + + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ + + self.curve.dgammadashdash_by_dcoeff_vjp(grad2) \ + + self.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ + + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + +class FrameRotation(Optimizable): + + def __init__(self, quadpoints, order, scale=1., dofs=None): + """ + Defines the rotation angle with respect to a reference orthonormal + frame (either frenet or centroid). For example, can be used to + define the rotation of a multifilament pack; alpha in Figure 1 of + Singh et al, "Optimization of finite-build stellarator coils", + Journal of Plasma Physics 86 (2020), + doi:10.1017/S0022377820000756 + """ + self.order = order + if dofs is None: + super().__init__(x0=np.zeros((2*order+1, ))) + else: + super().__init__(dofs=dofs) + self.quadpoints = quadpoints + self.scale = scale + self.jac = rotation_dcoeff(quadpoints, order) + self.jacdash = rotationdash_dcoeff(quadpoints, order) + self.jax_alpha = jit(lambda dofs, points: jaxrotation_pure(dofs, points, self.order)) + self.jax_alphadash = jit(lambda dofs, points: jaxrotationdash_pure(dofs, points, self.order)) + + def alpha(self, quadpoints): + return self.scale * self.jax_alpha(self._dofs.full_x, quadpoints) + + def alphadash(self, quadpoints): + return self.scale * self.jax_alphadash(self._dofs.full_x, quadpoints) + + def dalpha_by_dcoeff_vjp(self, quadpoints, v): + return Derivative({self: self.scale * sopp.vjp(v, self.jac)}) + + def dalphadash_by_dcoeff_vjp(self, quadpoints, v): + return Derivative({self: self.scale * sopp.vjp(v, self.jacdash)}) + + +class ZeroRotation(Optimizable): + + def __init__(self, quadpoints): + """ + Dummy class that just returns zero for the rotation angle. Equivalent to using + + .. code-block:: python + + rot = FilamentRotation(...) + rot.fix_all() + + """ + super().__init__() + self.zero = np.zeros((quadpoints.size, )) + + def alpha(self, quadpoints): + return self.zero + + def alphadash(self, quadpoints): + return self.zero + + def dalpha_by_dcoeff_vjp(self, quadpoints, v): + return Derivative({}) + + def dalphadash_by_dcoeff_vjp(self, quadpoints, v): + return Derivative({}) + + +@jit +def rotated_centroid_frame(gamma, gammadash, alpha): + t = gammadash + t *= 1./jnp.linalg.norm(gammadash, axis=1)[:, None] + R = jnp.mean(gamma, axis=0) # centroid + delta = gamma - R[None, :] + n = delta - jnp.sum(delta * t, axis=1)[:, None] * t + n *= 1./jnp.linalg.norm(n, axis=1)[:, None] + b = jnp.cross(t, n, axis=1) + + # now rotate the frame by alpha + nn = jnp.cos(alpha)[:, None] * n - jnp.sin(alpha)[:, None] * b + bb = jnp.sin(alpha)[:, None] * n + jnp.cos(alpha)[:, None] * b + return t, nn, bb + + +rotated_centroid_frame_dash = jit( + lambda gamma, gammadash, gammadashdash, alpha, alphadash: jvp(rotated_centroid_frame, + (gamma, gammadash, alpha), + (gammadash, gammadashdash, alphadash))[1]) + +rotated_centroid_frame_dcoeff_vjp0 = jit( + lambda gamma, gammadash, alpha, v: vjp( + lambda g: rotated_centroid_frame(g, gammadash, alpha), gamma)[1](v)[0]) + +rotated_centroid_frame_dcoeff_vjp1 = jit( + lambda gamma, gammadash, alpha, v: vjp( + lambda gd: rotated_centroid_frame(gamma, gd, alpha), gammadash)[1](v)[0]) + +rotated_centroid_frame_dcoeff_vjp2 = jit( + lambda gamma, gammadash, alpha, v: vjp( + lambda a: rotated_centroid_frame(gamma, gammadash, a), alpha)[1](v)[0]) + +rotated_centroid_frame_dash_dcoeff_vjp0 = jit( + lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: rotated_centroid_frame_dash(g, gammadash, gammadashdash, alpha, alphadash), gamma)[1](v)[0]) + +rotated_centroid_frame_dash_dcoeff_vjp1 = jit( + lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda gd: rotated_centroid_frame_dash(gamma, gd, gammadashdash, alpha, alphadash), gammadash)[1](v)[0]) + +rotated_centroid_frame_dash_dcoeff_vjp2 = jit( + lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda gdd: rotated_centroid_frame_dash(gamma, gammadash, gdd, alpha, alphadash), gammadashdash)[1](v)[0]) + +rotated_centroid_frame_dash_dcoeff_vjp3 = jit( + lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda a: rotated_centroid_frame_dash(gamma, gammadash, gammadashdash, a, alphadash), alpha)[1](v)[0]) + +rotated_centroid_frame_dash_dcoeff_vjp4 = jit( + lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda ad: rotated_centroid_frame_dash(gamma, gammadash, gammadashdash, alpha, ad), alphadash)[1](v)[0]) + + +@jit +def rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha): + """Frenet frame of a curve rotated by a angle that varies along the coil path""" + + N = gamma.shape[0] + t, n, b = (np.zeros((N, 3)), np.zeros((N, 3)), np.zeros((N, 3))) + t = gammadash + t *= 1./jnp.linalg.norm(gammadash, axis=1)[:, None] + + tdash = (1./jnp.linalg.norm(gammadash, axis=1)[:, None])**2 * (jnp.linalg.norm(gammadash, axis=1)[:, None] * gammadashdash + - (inner(gammadash, gammadashdash)/jnp.linalg.norm(gammadash, axis=1))[:, None] * gammadash) + + n = tdash + n *= 1/jnp.linalg.norm(tdash, axis=1)[:, None] + b = jnp.cross(t, n, axis=1) + # now rotate the frame by alpha + nn = jnp.cos(alpha)[:, None] * n - jnp.sin(alpha)[:, None] * b + bb = jnp.sin(alpha)[:, None] * n + jnp.cos(alpha)[:, None] * b + + return t, nn, bb + + +rotated_frenet_frame_dash = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: jvp(rotated_frenet_frame, + (gamma, gammadash, + gammadashdash, alpha), + (gammadash, gammadashdash, gammadashdashdash, alphadash))[1]) + +rotated_frenet_frame_dcoeff_vjp0 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda g: rotated_frenet_frame(g, gammadash, gammadashdash, alpha), gamma)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp1 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda gd: rotated_frenet_frame(gamma, gd, gammadashdash, alpha), gammadash)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp2 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda gdd: rotated_frenet_frame(gamma, gammadash, gdd, alpha), gammadashdash)[1](v)[0]) + +rotated_frenet_frame_dcoeff_vjp3 = jit( + lambda gamma, gammadash, gammadashdash, alpha, v: vjp( + lambda a: rotated_frenet_frame(gamma, gammadash, gammadashdash, a), alpha)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp0 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda g: rotated_frenet_frame_dash(g, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash), gamma)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp1 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gd: rotated_frenet_frame_dash(gamma, gd, gammadashdash, gammadashdashdash, alpha, alphadash), gammadash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp2 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gdd: rotated_frenet_frame_dash(gamma, gammadash, gdd, gammadashdashdash, alpha, alphadash), gammadashdash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp3 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda gddd: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gddd, alpha, alphadash), gammadashdashdash)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp4 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda a: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, a, alphadash), alpha)[1](v)[0]) + +rotated_frenet_frame_dash_dcoeff_vjp5 = jit( + lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( + lambda ad: rotated_frenet_frame_dash(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, ad), alphadash)[1](v)[0]) + + +def jaxrotation_pure(dofs, points, order): + rotation = jnp.zeros((len(points), )) + rotation += dofs[0] + for j in range(1, order+1): + rotation += dofs[2*j-1] * jnp.sin(2*np.pi*j*points) + rotation += dofs[2*j] * jnp.cos(2*np.pi*j*points) + return rotation + + +def jaxrotationdash_pure(dofs, points, order): + rotation = jnp.zeros((len(points), )) + for j in range(1, order+1): + rotation += dofs[2*j-1] * 2*np.pi*j*jnp.cos(2*np.pi*j*points) + rotation -= dofs[2*j] * 2*np.pi*j*jnp.sin(2*np.pi*j*points) + return rotation + + +def rotation_dcoeff(points, order): + jac = np.zeros((len(points), 2*order+1)) + jac[:, 0] = 1 + for j in range(1, order+1): + jac[:, 2*j-1] = np.sin(2*np.pi*j*points) + jac[:, 2*j+0] = np.cos(2*np.pi*j*points) + return jac + + +def rotationdash_dcoeff(points, order): + jac = np.zeros((len(points), 2*order+1)) + for j in range(1, order+1): + jac[:, 2*j-1] = +2*np.pi*j*np.cos(2*np.pi*j*points) + jac[:, 2*j+0] = -2*np.pi*j*np.sin(2*np.pi*j*points) + return jac + + +def inner(a, b): + """Inner product for arrays of shape (N, 3)""" + return np.sum(a*b, axis=1) + + +torsion2vjp0 = jit(lambda ndash, b, v: vjp( + lambda nd: torsion_pure(nd, b), ndash)[1](v)[0]) +torsion2vjp1 = jit(lambda ndash, b, v: vjp( + lambda bi: torsion_pure(ndash, bi), b)[1](v)[0]) + + +def torsion_pure_frenet(gamma, gammadash, gammadashdash, gammadashdashdash, + alpha, alphadash): + """Torsion function for export/evaulate coil sets""" + + _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + _, ndash, _ = rotated_frenet_frame_dash( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + + ndash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] + return inner(ndash, b) + + +def binormal_curvature_pure_frenet(gamma, gammadash, gammadashdash, gammadashdashdash, + alpha, alphadash): + """Binormal curvature function for export/evaulate coil sets.""" + + _, _, b = rotated_frenet_frame(gamma, gammadash, gammadashdash, alpha) + tdash, _, _ = rotated_frenet_frame_dash( + gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash) + + tdash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] + return inner(tdash, b) + + +def torsion_pure_centroid(gamma, gammadash, gammadashdash, + alpha, alphadash): + """Torsion function for export/evaulate coil sets""" + + _, _, b = rotated_centroid_frame(gamma, gammadash, alpha) + _, ndash, _ = rotated_centroid_frame_dash( + gamma, gammadash, gammadashdash, alpha, alphadash) + + ndash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] + return inner(ndash, b) + + +def binormal_curvature_pure_centroid(gamma, gammadash, gammadashdash, + alpha, alphadash): + """Binormal curvature function for export/evaulate coil sets.""" + + _, _, b = rotated_centroid_frame(gamma, gammadash, alpha) + tdash, _, _ = rotated_centroid_frame_dash( + gamma, gammadash, gammadashdash, alpha, alphadash) + + tdash *= 1/jnp.linalg.norm(gammadash, axis=1)[:, None] + return inner(tdash, b) diff --git a/src/simsopt/geo/strain_optimization_classes.py b/src/simsopt/geo/strain_optimization_classes.py index 7ec0dff66..2639d36f6 100644 --- a/src/simsopt/geo/strain_optimization_classes.py +++ b/src/simsopt/geo/strain_optimization_classes.py @@ -1,7 +1,3 @@ -""" -Implements strain optimization for HTS coils -""" - import numpy as np import jax.numpy as jnp from jax import vjp, jvp, grad @@ -10,99 +6,87 @@ from simsopt.geo import ZeroRotation, Curve from simsopt._core import Optimizable from simsopt._core.derivative import derivative_dec -from simsopt.geo.curveobjectives import Lp_curvature_pure +from simsopt.geo.curveobjectives import Lp_torsion_pure -# class TorsionalStrainPenalty(Optimizable): +class LPBinormalCurvatureStrainPenalty(Optimizable): + def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): + self.framedcurve = framedcurve + self.strain = StrainOpt(framedcurve,width) + self.width = width + self.p = p + self.threshold = threshold + self.J_jax = jit(lambda binorm, gammadash: Lp_torsion_pure(binorm, gammadash, p, threshold)) + self.grad0 = jit(lambda binorm, gammadash: grad(self.J_jax, argnums=0)(binorm, gammadash)) + self.grad1 = jit(lambda binorm, gammadash: grad(self.J_jax, argnums=1)(binorm, gammadash)) + super().__init__(depends_on=[framedcurve]) -# def __init__(self, curvefilament, width=3, max_strain): -# self.curvefilament = curvefilament -# self.width = width -# self.max_strain = max_strain -# super().__init__(depends_on=[curvefilament]) + def J(self): + return self.J_jax(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) -# def J(self): -# return Lp_curvature_pure(self.curvefilament.torsional_strain, -# self.curvefilament.curve.gammadash, p, self.max_strain) + @derivative_dec + def dJ(self): + grad0 = self.grad0(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) + grad1 = self.grad1(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) + vjp0 = self.strain.binormstrain_vjp(self.framedcurve.frame_binormal_curvature(),self.width,grad0) + return self.framedcurve.dframe_binormal_curvature_by_dcoeff_vjp(vjp0) \ + + self.framedcurve.curve.dgammadash_by_dcoeff_vjp(grad1) + + return_fn_map = {'J': J, 'dJ': dJ} + +class LPTorsionalStrainPenalty(Optimizable): + + def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): + self.framedcurve = framedcurve + self.strain = StrainOpt(framedcurve,width) + self.width = width + self.p = p + self.threshold = threshold + self.J_jax = jit(lambda torsion, gammadash: Lp_torsion_pure(torsion, gammadash, p, threshold)) + self.grad0 = jit(lambda torsion, gammadash: grad( + self.J_jax, argnums=0)(torsion, gammadash)) + self.grad1 = jit(lambda torsion, gammadash: grad( + self.J_jax, argnums=1)(torsion, gammadash)) + super().__init__(depends_on=[framedcurve]) -# @derivative_dec -# def dJ(self): + def J(self): + return self.J_jax(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) + @derivative_dec + def dJ(self): + grad0 = self.grad0(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) + grad1 = self.grad1(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) + vjp0 = self.strain.torstrain_vjp(self.framedcurve.frame_torsion(),self.width,grad0) + return self.framedcurve.dframe_torsion_by_dcoeff_vjp(vjp0) \ + + self.framedcurve.curve.dgammadash_by_dcoeff_vjp(grad1) + + return_fn_map = {'J': J, 'dJ': dJ} class StrainOpt(Optimizable): - """Class for strain optimization""" - def __init__(self, framedcurve, width=3): + def __init__(self, framedcurve, width=1e-3): self.framedcurve = framedcurve self.width = width - self.J_jax = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: strain_opt_pure( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) - self.thisgrad0 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( - self.J_jax, argnums=0)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) - self.thisgrad1 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( - self.J_jax, argnums=1)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) - self.thisgrad2 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( - self.J_jax, argnums=2)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) - self.thisgrad3 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( - self.J_jax, argnums=3)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) - self.thisgrad4 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( - self.J_jax, argnums=4)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) - self.thisgrad5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width: grad( - self.J_jax, argnums=5)(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, width)) + self.torstrain_jax = jit(lambda torsion, width: torstrain_pure( + torsion, width)) + self.binormstrain_jax = jit(lambda binorm, width: binormstrain_pure( + binorm, width)) + self.torstrain_vjp = jit(lambda torsion, width, v: vjp( + lambda g: torstrain_pure(g, width), torsion)[1](v)[0]) + self.binormstrain_vjp = jit(lambda binorm, width, v: vjp( + lambda g: binormstrain_pure(g, width), binorm)[1](v)[0]) + super().__init__(depends_on=[framedcurve]) def torsional_strain(self): - """Exports torsion along a coil for a StrainOpt object""" - torsion = self.framedcurve.frame_torsion() - return torsion**2 * self.width**2 / 12 # From 2020 Paz-Soldan + return self.torstrain_jax(self.framedcurve.frame_torsion(),self.width) def binormal_curvature_strain(self): - binormal_curvature = self.framedcurve.frame_binormal_curvature() - return (self.width/2)*binormal_curvature # From 2020 Paz-Soldan - - # def J(self): - # """ - # This returns the value of the quantity. - # """ - # gamma = self.curve.curve.gamma() - # d1gamma = self.curve.curve.gammadash() - # d2gamma = self.curve.curve.gammadashdash() - # d3gamma = self.curve.curve.gammadashdashdash() - # alpha = self.curve.rotation.alpha(self.curve.quadpoints) - # alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - # width = self.width - - # return self.J_jax(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash, width) - - # @derivative_dec - # def dJ(self): - # """ - # This returns the derivative of the quantity with respect to the curve dofs. - # """ - # gamma = self.curve.curve.gamma() - # d1gamma = self.curve.curve.gammadash() - # d2gamma = self.curve.curve.gammadashdash() - # d3gamma = self.curve.curve.gammadashdashdash() - # alpha = self.curve.rotation.alpha(self.curve.quadpoints) - # alphadash = self.curve.rotation.alphadash(self.curve.quadpoints) - # width = self.width - - # grad0 = self.thisgrad0(gamma, d1gamma, d2gamma, - # d3gamma, alpha, alphadash, width) - # grad1 = self.thisgrad1(gamma, d1gamma, d2gamma, - # d3gamma, alpha, alphadash, width) - # grad2 = self.thisgrad2(gamma, d1gamma, d2gamma, - # d3gamma, alpha, alphadash, width) - # grad3 = self.thisgrad3(gamma, d1gamma, d2gamma, - # d3gamma, alpha, alphadash, width) - # grad4 = self.thisgrad4(gamma, d1gamma, d2gamma, - # d3gamma, alpha, alphadash, width) - # grad5 = self.thisgrad5(gamma, d1gamma, d2gamma, - # d3gamma, alpha, alphadash, width) + return self.binormstrain_jax(self.framedcurve.frame_binormal_curvature(),self.width) - # return self.curve.curve.dgamma_by_dcoeff_vjp(grad0) + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ - # + self.curve.curve.dgammadashdash_by_dcoeff_vjp(grad2) + self.curve.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ - # + self.curve.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) + \ - # self.curve.rotation.dalphadash_by_dcoeff_vjp( - # self.curve.quadpoints, grad5) +@jit +def torstrain_pure(torsion, width): + return torsion**2 * width**2 / 12 - # return_fn_map = {'J': J, 'dJ': dJ} +@jit +def binormstrain_pure(binorm, width): + return (width / 2) * jnp.abs(binorm) From d09d9091e91f855947b6fd2f3487098258c67477 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Mon, 7 Aug 2023 12:54:56 -0400 Subject: [PATCH 12/61] Adding more documentation. --- src/simsopt/geo/framedcurve.py | 7 +- .../geo/strain_optimization_classes.py | 66 ++++++++++++++++++- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index 9fe627a58..02991125c 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -16,7 +16,8 @@ class FramedCurve(sopp.Curve, Curve): def __init__(self, curve, rotation=None): """ - A framed curve defines an orthonormal frame around a Curve. + A FramedCurve defines an orthonormal basis around a Curve, + where one basis is taken to be the tangent along the Curve. The frame is defined with respect to a reference frame, either centroid or frenet. A rotation angle defines the rotation with respect to this reference frame. @@ -33,7 +34,6 @@ def __init__(self, curve, rotation=None): class FramedCurveFrenet(FramedCurve): - """ Given a curve, one defines a reference frame using the Frenet normal and binormal vectors: @@ -45,7 +45,6 @@ class FramedCurveFrenet(FramedCurve): In addition, we specify an angle along the curve that defines the rotation with respect to this reference frame. """ - def __init__(self, curve, rotation=None): FramedCurve.__init__(self, curve, rotation) @@ -176,7 +175,6 @@ def dframe_binormal_curvature_by_dcoeff_vjp(self, v): class FramedCurveCentroid(FramedCurve): - """ Implementation of the centroid frame introduced in Singh et al, "Optimization of finite-build stellarator coils", @@ -189,7 +187,6 @@ class FramedCurveCentroid(FramedCurve): The idea is explained well in Figure 1 in the reference above. """ - def __init__(self, curve, rotation=None): FramedCurve.__init__(self, curve, rotation) self.rotated_frame = rotated_centroid_frame diff --git a/src/simsopt/geo/strain_optimization_classes.py b/src/simsopt/geo/strain_optimization_classes.py index 2639d36f6..5790aa618 100644 --- a/src/simsopt/geo/strain_optimization_classes.py +++ b/src/simsopt/geo/strain_optimization_classes.py @@ -9,6 +9,15 @@ from simsopt.geo.curveobjectives import Lp_torsion_pure class LPBinormalCurvatureStrainPenalty(Optimizable): + r""" + This class computes a penalty term based on the :math:`L_p` norm + of the binormal curvature strain, and penalizes where the local strain exceeds a threshold + + .. math:: + J = \frac{1}{p} \int_{\text{curve}} \text{max}(\epsilon_{\text{bend}} - \epsilon_0, 0)^p ~dl + + where :math:`\epsilon_0` is a threshold strain, given by the argument ``threshold``. + """ def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.framedcurve = framedcurve self.strain = StrainOpt(framedcurve,width) @@ -20,11 +29,17 @@ def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.grad1 = jit(lambda binorm, gammadash: grad(self.J_jax, argnums=1)(binorm, gammadash)) super().__init__(depends_on=[framedcurve]) - def J(self): + def J(self): + """ + This returns the value of the quantity. + """ return self.J_jax(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) @derivative_dec def dJ(self): + """ + This returns the derivative of the quantity with respect to the curve and rotation dofs. + """ grad0 = self.grad0(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) grad1 = self.grad1(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) vjp0 = self.strain.binormstrain_vjp(self.framedcurve.frame_binormal_curvature(),self.width,grad0) @@ -34,7 +49,15 @@ def dJ(self): return_fn_map = {'J': J, 'dJ': dJ} class LPTorsionalStrainPenalty(Optimizable): + r""" + This class computes a penalty term based on the :math:`L_p` norm + of the torsional strain, and penalizes where the local strain exceeds a threshold + .. math:: + J = \frac{1}{p} \int_{\text{curve}} \text{max}(\epsilon_{\text{tor}} - \epsilon_0, 0)^p ~dl + + where :math:`\epsilon_0` is a threshold strain, given by the argument ``threshold``. + """ def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.framedcurve = framedcurve self.strain = StrainOpt(framedcurve,width) @@ -49,10 +72,16 @@ def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): super().__init__(depends_on=[framedcurve]) def J(self): + """ + This returns the value of the quantity. + """ return self.J_jax(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) @derivative_dec def dJ(self): + """ + This returns the derivative of the quantity with respect to the curve and rotation dofs. + """ grad0 = self.grad0(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) grad1 = self.grad1(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) vjp0 = self.strain.torstrain_vjp(self.framedcurve.frame_torsion(),self.width,grad0) @@ -62,7 +91,27 @@ def dJ(self): return_fn_map = {'J': J, 'dJ': dJ} class StrainOpt(Optimizable): + r""" + This class evaluates the torsional and binormal curvature strains on HTS, based on + a filamentary model of the coil and the orientation of the HTX tape. + + As defined in, + + Paz Soldan, "Non-planar coil winding angle optimization for compatibility with + non-insulated high-temperature superconducting magnets", Journal of Plasma Physics + 86 (2020), doi:10.1017/S0022377820001208, + + the expressions for the strains are: + + .. math:: + \epsilon_{\text{tor}} = \frac{\tau^2 w^2}{12} + \epsilon_{\text{bend}} = \frac{w |\hat{\textbf{b}} \cdot \boldsymbol{\kappa}|}{2}, + + where :math:`\tau` is the torsion of the tape frame, :math:`\hat{\textbf{b}}` is the + frame binormal vector, and :math:`\boldsymbol{\kappa}` is the curvature vector of the + filamentary coil. + """ def __init__(self, framedcurve, width=1e-3): self.framedcurve = framedcurve self.width = width @@ -78,15 +127,30 @@ def __init__(self, framedcurve, width=1e-3): super().__init__(depends_on=[framedcurve]) def torsional_strain(self): + """ + Returns the value of the torsional strain, :math:`\epsilon_{\text{tor}}`, along + the quadpoints defining the filamentary coil. + """ return self.torstrain_jax(self.framedcurve.frame_torsion(),self.width) def binormal_curvature_strain(self): + """ + Returns the value of the torsional strain, :math:`\epsilon_{\text{bend}}`, along + the quadpoints defining the filamentary coil. + """ return self.binormstrain_jax(self.framedcurve.frame_binormal_curvature(),self.width) @jit def torstrain_pure(torsion, width): + """ + This function is used in a Python+Jax implementation of the LPTorsionalStrainPenalty objective. + """ return torsion**2 * width**2 / 12 @jit def binormstrain_pure(binorm, width): + """ + This function is used in a Python+Jax implementation of the LPBinormalCurvatureStrainPenalty + objective. + """ return (width / 2) * jnp.abs(binorm) From 818d041f9a10383288584b11355e99cc923472bf Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Mon, 7 Aug 2023 12:57:11 -0400 Subject: [PATCH 13/61] Autopep. --- CMakeLists.txt | 2 +- examples/3_Advanced/strain_simple.py | 2 +- src/simsopt/geo/finitebuild.py | 3 +- src/simsopt/geo/framedcurve.py | 45 ++++++++++--------- .../geo/strain_optimization_classes.py | 40 ++++++++++------- 5 files changed, 52 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d1ee83e1..a488b5423 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ else() unset(COMPILER_SUPPORTS_MARCH_NATIVE CACHE) CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) if(COMPILER_SUPPORTS_MARCH_NATIVE) - set(CMAKE_CXX_FLAGS "-O3 -march=native -mfma -ffp-contract=fast") + set(CMAKE_CXX_FLAGS "-O3 -mfma -ffp-contract=fast") elseif(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "arm64") set(CMAKE_CXX_FLAGS "-O3 -mcpu=apple-a14 -mfma -ffp-contract=fast") else() diff --git a/examples/3_Advanced/strain_simple.py b/examples/3_Advanced/strain_simple.py index f11fac807..23214d37d 100644 --- a/examples/3_Advanced/strain_simple.py +++ b/examples/3_Advanced/strain_simple.py @@ -30,7 +30,7 @@ width = 12 -rotation = FrameRotation(curve.quadpoints,rot_order) +rotation = FrameRotation(curve.quadpoints, rot_order) framedcurve = FramedCurveFrenet(curve, rotation) strain = StrainOpt(framedcurve, width=width) diff --git a/src/simsopt/geo/finitebuild.py b/src/simsopt/geo/finitebuild.py index 091d2f364..1ebfb154f 100644 --- a/src/simsopt/geo/finitebuild.py +++ b/src/simsopt/geo/finitebuild.py @@ -14,7 +14,7 @@ approximation of finite build coils. """ -__all__ = ['create_multifilament_grid','CurveFilament'] +__all__ = ['create_multifilament_grid', 'CurveFilament'] class CurveFilament(FramedCurve): @@ -109,6 +109,7 @@ def dgammadash_by_dcoeff_vjp(self, v): out += self.curve.dgammadashdashdash_by_dcoeff_vjp(vgddd) return out + def create_multifilament_grid(curve, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, rotation_order=None, rotation_scaling=None, frame='centroid'): """ diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index 02991125c..3a5611a1a 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -45,6 +45,7 @@ class FramedCurveFrenet(FramedCurve): In addition, we specify an angle along the curve that defines the rotation with respect to this reference frame. """ + def __init__(self, curve, rotation=None): FramedCurve.__init__(self, curve, rotation) @@ -126,17 +127,17 @@ def dframe_torsion_by_dcoeff_vjp(self, v): alphadash = self.rotation.alphadash(self.curve.quadpoints) grad0 = self.torsiongrad_vjp0(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad1 = self.torsiongrad_vjp1(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad2 = self.torsiongrad_vjp2(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad3 = self.torsiongrad_vjp3(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad4 = self.torsiongrad_vjp4(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad5 = self.torsiongrad_vjp5(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) return self.curve.dgamma_by_dcoeff_vjp(grad0) \ + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ @@ -154,17 +155,17 @@ def dframe_binormal_curvature_by_dcoeff_vjp(self, v): alphadash = self.rotation.alphadash(self.curve.quadpoints) grad0 = self.binormgrad_vjp0(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad1 = self.binormgrad_vjp1(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad2 = self.binormgrad_vjp2(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad3 = self.binormgrad_vjp3(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad4 = self.binormgrad_vjp4(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) grad5 = self.binormgrad_vjp5(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash, v) + d3gamma, alpha, alphadash, v) return self.curve.dgamma_by_dcoeff_vjp(grad0) \ + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ @@ -187,6 +188,7 @@ class FramedCurveCentroid(FramedCurve): The idea is explained well in Figure 1 in the reference above. """ + def __init__(self, curve, rotation=None): FramedCurve.__init__(self, curve, rotation) self.rotated_frame = rotated_centroid_frame @@ -196,14 +198,14 @@ def __init__(self, curve, rotation=None): self.torsiongrad_vjp0 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(g, gammadash, gammadashdash, alpha, alphadash), gamma)[1](v)[0]) self.torsiongrad_vjp1 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( - lambda g: self.torsion(gamma, g, gammadashdash, alpha, alphadash), gammadash)[1](v)[0]) + lambda g: self.torsion(gamma, g, gammadashdash, alpha, alphadash), gammadash)[1](v)[0]) self.torsiongrad_vjp2 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, g, alpha, alphadash), gammadashdash)[1](v)[0]) self.torsiongrad_vjp3 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, gammadashdash, g, alphadash), alpha)[1](v)[0]) self.torsiongrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, gammadashdash, alpha, g), alphadash)[1](v)[0]) - + self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( gamma, gammadash, gammadashdash, alpha, alphadash)) self.rotated_frame_dcoeff_vjp0 = rotated_centroid_frame_dcoeff_vjp0 @@ -250,15 +252,15 @@ def d_frame_torsion(self): alphadash = self.rotation.alphadash(self.curve.quadpoints) grad0 = self.torsiongrad0(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) + d3gamma, alpha, alphadash) grad1 = self.torsiongrad1(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) + d3gamma, alpha, alphadash) grad2 = self.torsiongrad2(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) + d3gamma, alpha, alphadash) grad4 = self.torsiongrad4(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) + d3gamma, alpha, alphadash) grad5 = self.torsiongrad5(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) + d3gamma, alpha, alphadash) return self.curve.dgamma_by_dcoeff_vjp(grad0) \ + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ @@ -267,6 +269,7 @@ def d_frame_torsion(self): + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + class FrameRotation(Optimizable): def __init__(self, quadpoints, order, scale=1., dofs=None): @@ -349,8 +352,8 @@ def rotated_centroid_frame(gamma, gammadash, alpha): rotated_centroid_frame_dash = jit( lambda gamma, gammadash, gammadashdash, alpha, alphadash: jvp(rotated_centroid_frame, - (gamma, gammadash, alpha), - (gammadash, gammadashdash, alphadash))[1]) + (gamma, gammadash, alpha), + (gammadash, gammadashdash, alphadash))[1]) rotated_centroid_frame_dcoeff_vjp0 = jit( lambda gamma, gammadash, alpha, v: vjp( diff --git a/src/simsopt/geo/strain_optimization_classes.py b/src/simsopt/geo/strain_optimization_classes.py index 5790aa618..65ebc6954 100644 --- a/src/simsopt/geo/strain_optimization_classes.py +++ b/src/simsopt/geo/strain_optimization_classes.py @@ -8,6 +8,7 @@ from simsopt._core.derivative import derivative_dec from simsopt.geo.curveobjectives import Lp_torsion_pure + class LPBinormalCurvatureStrainPenalty(Optimizable): r""" This class computes a penalty term based on the :math:`L_p` norm @@ -18,9 +19,10 @@ class LPBinormalCurvatureStrainPenalty(Optimizable): where :math:`\epsilon_0` is a threshold strain, given by the argument ``threshold``. """ + def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.framedcurve = framedcurve - self.strain = StrainOpt(framedcurve,width) + self.strain = StrainOpt(framedcurve, width) self.width = width self.p = p self.threshold = threshold @@ -33,21 +35,22 @@ def J(self): """ This returns the value of the quantity. """ - return self.J_jax(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) + return self.J_jax(self.strain.binormal_curvature_strain(), self.framedcurve.curve.gammadash()) @derivative_dec def dJ(self): """ This returns the derivative of the quantity with respect to the curve and rotation dofs. """ - grad0 = self.grad0(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) - grad1 = self.grad1(self.strain.binormal_curvature_strain(),self.framedcurve.curve.gammadash()) - vjp0 = self.strain.binormstrain_vjp(self.framedcurve.frame_binormal_curvature(),self.width,grad0) + grad0 = self.grad0(self.strain.binormal_curvature_strain(), self.framedcurve.curve.gammadash()) + grad1 = self.grad1(self.strain.binormal_curvature_strain(), self.framedcurve.curve.gammadash()) + vjp0 = self.strain.binormstrain_vjp(self.framedcurve.frame_binormal_curvature(), self.width, grad0) return self.framedcurve.dframe_binormal_curvature_by_dcoeff_vjp(vjp0) \ - + self.framedcurve.curve.dgammadash_by_dcoeff_vjp(grad1) - + + self.framedcurve.curve.dgammadash_by_dcoeff_vjp(grad1) + return_fn_map = {'J': J, 'dJ': dJ} + class LPTorsionalStrainPenalty(Optimizable): r""" This class computes a penalty term based on the :math:`L_p` norm @@ -58,9 +61,10 @@ class LPTorsionalStrainPenalty(Optimizable): where :math:`\epsilon_0` is a threshold strain, given by the argument ``threshold``. """ + def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.framedcurve = framedcurve - self.strain = StrainOpt(framedcurve,width) + self.strain = StrainOpt(framedcurve, width) self.width = width self.p = p self.threshold = threshold @@ -75,21 +79,22 @@ def J(self): """ This returns the value of the quantity. """ - return self.J_jax(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) + return self.J_jax(self.strain.torsional_strain(), self.framedcurve.curve.gammadash()) @derivative_dec def dJ(self): """ This returns the derivative of the quantity with respect to the curve and rotation dofs. """ - grad0 = self.grad0(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) - grad1 = self.grad1(self.strain.torsional_strain(),self.framedcurve.curve.gammadash()) - vjp0 = self.strain.torstrain_vjp(self.framedcurve.frame_torsion(),self.width,grad0) + grad0 = self.grad0(self.strain.torsional_strain(), self.framedcurve.curve.gammadash()) + grad1 = self.grad1(self.strain.torsional_strain(), self.framedcurve.curve.gammadash()) + vjp0 = self.strain.torstrain_vjp(self.framedcurve.frame_torsion(), self.width, grad0) return self.framedcurve.dframe_torsion_by_dcoeff_vjp(vjp0) \ - + self.framedcurve.curve.dgammadash_by_dcoeff_vjp(grad1) - + + self.framedcurve.curve.dgammadash_by_dcoeff_vjp(grad1) + return_fn_map = {'J': J, 'dJ': dJ} + class StrainOpt(Optimizable): r""" This class evaluates the torsional and binormal curvature strains on HTS, based on @@ -112,6 +117,7 @@ class StrainOpt(Optimizable): filamentary coil. """ + def __init__(self, framedcurve, width=1e-3): self.framedcurve = framedcurve self.width = width @@ -131,14 +137,15 @@ def torsional_strain(self): Returns the value of the torsional strain, :math:`\epsilon_{\text{tor}}`, along the quadpoints defining the filamentary coil. """ - return self.torstrain_jax(self.framedcurve.frame_torsion(),self.width) + return self.torstrain_jax(self.framedcurve.frame_torsion(), self.width) def binormal_curvature_strain(self): """ Returns the value of the torsional strain, :math:`\epsilon_{\text{bend}}`, along the quadpoints defining the filamentary coil. """ - return self.binormstrain_jax(self.framedcurve.frame_binormal_curvature(),self.width) + return self.binormstrain_jax(self.framedcurve.frame_binormal_curvature(), self.width) + @jit def torstrain_pure(torsion, width): @@ -147,6 +154,7 @@ def torstrain_pure(torsion, width): """ return torsion**2 * width**2 / 12 + @jit def binormstrain_pure(binorm, width): """ From 87e8e7e7ecc8ed0c41e5c959a00eac55af029d5c Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Tue, 8 Aug 2023 09:22:49 -0400 Subject: [PATCH 14/61] Tests now passing. --- src/simsopt/geo/finitebuild.py | 54 +++-------- src/simsopt/geo/framedcurve.py | 129 ++++++++++++++++++++----- tests/geo/test_finitebuild.py | 172 ++++++++++++++++++--------------- 3 files changed, 213 insertions(+), 142 deletions(-) diff --git a/src/simsopt/geo/finitebuild.py b/src/simsopt/geo/finitebuild.py index 1ebfb154f..3d1783e9d 100644 --- a/src/simsopt/geo/finitebuild.py +++ b/src/simsopt/geo/finitebuild.py @@ -7,7 +7,7 @@ from .._core.derivative import Derivative from .curve import Curve from .jit import jit -from .framedcurve import FramedCurve +from .framedcurve import FramedCurve, FrameRotation, ZeroRotation, FramedCurveCentroid, FramedCurveFrenet """ The functions and classes in this model are used to deal with multifilament @@ -51,61 +51,39 @@ def gamma_impl(self, gamma, quadpoints): assert quadpoints.shape[0] == self.curve.quadpoints.shape[0] assert np.linalg.norm(quadpoints - self.curve.quadpoints) < 1e-15 c = self.curve - t, n, b = self.rotated_frame(c.gamma(), c.gammadash(), self.rotation.alpha(c.quadpoints)) + t, n, b = self.framedcurve.rotated_frame() gamma[:] = self.curve.gamma() + self.dn * n + self.db * b def gammadash_impl(self, gammadash): - td, nd, bd = self.rotated_frame_dash() + td, nd, bd = self.framedcurve.rotated_frame_dash() gammadash[:] = self.curve.gammadash() + self.dn * nd + self.db * bd def dgamma_by_dcoeff_vjp(self, v): - g = self.curve.gamma() - gd = self.curve.gammadash() - gdd = self.curve.gammadashdash() - a = self.rotation.alpha(self.curve.quadpoints) - zero = np.zeros_like(v) - vg = self.framedcurve.rotated_frame_dcoeff_vjp0( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - vgd = self.framedcurve.rotated_frame_dcoeff_vjp1( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) - va = self.framedcurve.rotated_frame_dcoeff_vjp3( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + vg = self.framedcurve.rotated_frame_dcoeff_vjp(v, self.dn, self.db, 0) + vgd = self.framedcurve.rotated_frame_dcoeff_vjp(v, self.dn, self.db, 1) + vgdd = self.framedcurve.rotated_frame_dcoeff_vjp(v, self.dn, self.db, 2) + va = self.framedcurve.rotated_frame_dcoeff_vjp(v, self.dn, self.db, 3) out = self.curve.dgamma_by_dcoeff_vjp(v + vg) \ + self.curve.dgammadash_by_dcoeff_vjp(vgd) \ + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) - if self.framedcurve.rotated_frame_dcoeff_vjp2 is not None: - vgdd = self.framedcurve.rotated_frame_dcoeff_vjp2( - g, gd, gdd, a, (zero, self.dn*v, self.db*v)) + if vgdd is not None: out += self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) return out def dgammadash_by_dcoeff_vjp(self, v): - g = self.curve.gamma() - gd = self.curve.gammadash() - gdd = self.curve.gammadashdash() - gddd = self.curve.gammadashdashdash() - a = self.rotation.alpha(self.curve.quadpoints) - ad = self.rotation.alphadash(self.curve.quadpoints) - zero = np.zeros_like(v) - - vg = self.framedcurve.rotated_frame_dash_dcoeff_vjp0( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgd = self.framedcurve.rotated_frame_dash_dcoeff_vjp1( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vgdd = self.framedcurve.rotated_frame_dash_dcoeff_vjp2( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - va = self.framedcurve.rotated_frame_dash_dcoeff_vjp4( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) - vad = self.framedcurve.rotated_frame_dash_dcoeff_vjp5( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + + vg = self.framedcurve.rotated_frame_dash_dcoeff_vjp(v, self.dn, self.db, 0) + vgd = self.framedcurve.rotated_frame_dash_dcoeff_vjp(v, self.dn, self.db, 1) + vgdd = self.framedcurve.rotated_frame_dash_dcoeff_vjp(v, self.dn, self.db, 2) + vgddd = self.framedcurve.rotated_frame_dash_dcoeff_vjp(v, self.dn, self.db, 3) + va = self.framedcurve.rotated_frame_dash_dcoeff_vjp(v, self.dn, self.db, 4) + vad = self.framedcurve.rotated_frame_dash_dcoeff_vjp(v, self.dn, self.db, 5) out = self.curve.dgamma_by_dcoeff_vjp(vg) \ + self.curve.dgammadash_by_dcoeff_vjp(v+vgd) \ + self.curve.dgammadashdash_by_dcoeff_vjp(vgdd) \ + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, va) \ + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, vad) - if self.framedcurve.rotated_frame_dash_dcoeff_vjp3 is not None: - vgddd = self.framedcurve.rotated_frame_dash_dcoeff_vjp3( - g, gd, gdd, gddd, a, ad, (zero, self.dn*v, self.db*v)) + if vgddd is not None: out += self.curve.dgammadashdashdash_by_dcoeff_vjp(vgddd) return out diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index 3a5611a1a..b20237717 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -9,7 +9,7 @@ from .jit import jit __all__ = ['FramedCurve', 'FramedCurveFrenet', 'FramedCurveCentroid', - 'FrameRotation', 'ZeroRotation'] + 'FrameRotation', 'ZeroRotation', 'FramedCurve'] class FramedCurve(sopp.Curve, Curve): @@ -79,19 +79,11 @@ def __init__(self, curve, rotation=None): self.torsiongrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, g), alphadash)[1](v)[0]) - self.rotated_frame = rotated_frenet_frame self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: binormal_curvature_pure_frenet( gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) - self.rotated_frame_dcoeff_vjp0 = rotated_frenet_frame_dcoeff_vjp0 - self.rotated_frame_dcoeff_vjp1 = rotated_frenet_frame_dcoeff_vjp1 - self.rotated_frame_dcoeff_vjp2 = rotated_frenet_frame_dcoeff_vjp2 - self.rotated_frame_dcoeff_vjp3 = rotated_frenet_frame_dcoeff_vjp3 - self.rotated_frame_dash_dcoeff_vjp0 = rotated_frenet_frame_dash_dcoeff_vjp0 - self.rotated_frame_dash_dcoeff_vjp1 = rotated_frenet_frame_dash_dcoeff_vjp1 - self.rotated_frame_dash_dcoeff_vjp2 = rotated_frenet_frame_dash_dcoeff_vjp2 - self.rotated_frame_dash_dcoeff_vjp3 = rotated_frenet_frame_dash_dcoeff_vjp3 - self.rotated_frame_dash_dcoeff_vjp4 = rotated_frenet_frame_dash_dcoeff_vjp4 - self.rotated_frame_dash_dcoeff_vjp5 = rotated_frenet_frame_dash_dcoeff_vjp5 + + def rotated_frame(self): + return rotated_frenet_frame(self.curve.gamma(), self.curve.gammadash(), self.curve.gammadashdash(), self.rotation.alpha(self.curve.quadpoints)) def rotated_frame_dash(self): return rotated_frenet_frame_dash( @@ -174,6 +166,54 @@ def dframe_binormal_curvature_by_dcoeff_vjp(self, v): + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + def rotated_frame_dcoeff_vjp(self, v, dn, db, arg=0): + assert arg in [0, 1, 2, 3] + g = self.curve.gamma() + gd = self.curve.gammadash() + gdd = self.curve.gammadashdash() + a = self.rotation.alpha(self.curve.quadpoints) + zero = np.zeros_like(v) + if arg == 0: + return rotated_frenet_frame_dcoeff_vjp0( + g, gd, gdd, a, (zero, dn*v, db*v)) + elif arg == 1: + return rotated_frenet_frame_dcoeff_vjp1( + g, gd, gdd, a, (zero, dn*v, db*v)) + elif arg == 2: + return rotated_frenet_frame_dcoeff_vjp2( + g, gd, gdd, a, (zero, dn*v, db*v)) + elif arg == 3: + return rotated_frenet_frame_dcoeff_vjp3( + g, gd, gdd, a, (zero, dn*v, db*v)) + + def rotated_frame_dash_dcoeff_vjp(self, v, dn, db, arg=0): + assert arg in [0, 1, 2, 3, 4, 5] + g = self.curve.gamma() + gd = self.curve.gammadash() + gdd = self.curve.gammadashdash() + gddd = self.curve.gammadashdashdash() + a = self.rotation.alpha(self.curve.quadpoints) + ad = self.rotation.alphadash(self.curve.quadpoints) + zero = np.zeros_like(v) + if arg == 0: + return rotated_frenet_frame_dash_dcoeff_vjp0( + g, gd, gdd, gddd, a, ad, (zero, dn*v, db*v)) + if arg == 1: + return rotated_frenet_frame_dash_dcoeff_vjp1( + g, gd, gdd, gddd, a, ad, (zero, dn*v, db*v)) + if arg == 2: + return rotated_frenet_frame_dash_dcoeff_vjp2( + g, gd, gdd, gddd, a, ad, (zero, dn*v, db*v)) + if arg == 3: + return rotated_frenet_frame_dash_dcoeff_vjp3( + g, gd, gdd, gddd, a, ad, (zero, dn*v, db*v)) + if arg == 4: + return rotated_frenet_frame_dash_dcoeff_vjp4( + g, gd, gdd, gddd, a, ad, (zero, dn*v, db*v)) + if arg == 5: + return rotated_frenet_frame_dash_dcoeff_vjp5( + g, gd, gdd, gddd, a, ad, (zero, dn*v, db*v)) + class FramedCurveCentroid(FramedCurve): """ @@ -191,7 +231,6 @@ class FramedCurveCentroid(FramedCurve): def __init__(self, curve, rotation=None): FramedCurve.__init__(self, curve, rotation) - self.rotated_frame = rotated_centroid_frame self.torsion = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: torsion_pure_centroid( gamma, gammadash, gammadashdash, alpha, alphadash)) @@ -208,16 +247,6 @@ def __init__(self, curve, rotation=None): self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( gamma, gammadash, gammadashdash, alpha, alphadash)) - self.rotated_frame_dcoeff_vjp0 = rotated_centroid_frame_dcoeff_vjp0 - self.rotated_frame_dcoeff_vjp1 = rotated_centroid_frame_dcoeff_vjp1 - self.rotated_frame_dcoeff_vjp2 = None - self.rotated_frame_dcoeff_vjp3 = rotated_centroid_frame_dcoeff_vjp2 - self.rotated_frame_dash_dcoeff_vjp0 = rotated_centroid_frame_dash_dcoeff_vjp0 - self.rotated_frame_dash_dcoeff_vjp1 = rotated_centroid_frame_dash_dcoeff_vjp1 - self.rotated_frame_dash_dcoeff_vjp2 = rotated_centroid_frame_dash_dcoeff_vjp2 - self.rotated_frame_dash_dcoeff_vjp3 = None - self.rotated_frame_dash_dcoeff_vjp4 = rotated_centroid_frame_dash_dcoeff_vjp3 - self.rotated_frame_dash_dcoeff_vjp5 = rotated_centroid_frame_dash_dcoeff_vjp4 def frame_torsion(self): """Exports frame torsion along a curve""" @@ -238,6 +267,10 @@ def frame_binormal_curvature(self): alphadash = self.rotation.alphadash(self.curve.quadpoints) return self.binormal_curvature(gamma, d1gamma, d2gamma, alpha, alphadash) + def rotated_frame(self): + return rotated_centroid_frame(self.curve.gamma(), self.curve.gammadash(), + self.rotation.alpha(self.curve.quadpoints)) + def rotated_frame_dash(self): return rotated_centroid_frame_dash( self.curve.gamma(), self.curve.gammadash(), self.curve.gammadashdash(), @@ -269,6 +302,50 @@ def d_frame_torsion(self): + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + def rotated_frame_dcoeff_vjp(self, v, dn, db, arg=0): + assert arg in [0, 1, 2, 3] + g = self.curve.gamma() + gd = self.curve.gammadash() + a = self.rotation.alpha(self.curve.quadpoints) + zero = np.zeros_like(v) + if arg == 0: + return rotated_centroid_frame_dcoeff_vjp0( + g, gd, a, (zero, dn*v, db*v)) + if arg == 1: + return rotated_centroid_frame_dcoeff_vjp1( + g, gd, a, (zero, dn*v, db*v)) + if arg == 2: + return None + if arg == 3: + return rotated_centroid_frame_dcoeff_vjp3( + g, gd, a, (zero, dn*v, db*v)) + + def rotated_frame_dash_dcoeff_vjp(self, v, dn, db, arg=0): + assert arg in [0, 1, 2, 3, 4, 5] + g = self.curve.gamma() + gd = self.curve.gammadash() + gdd = self.curve.gammadashdash() + a = self.rotation.alpha(self.curve.quadpoints) + ad = self.rotation.alphadash(self.curve.quadpoints) + zero = np.zeros_like(v) + if arg == 0: + return rotated_centroid_frame_dash_dcoeff_vjp0( + g, gd, gdd, a, ad, (zero, dn*v, db*v)) + if arg == 1: + return rotated_centroid_frame_dash_dcoeff_vjp1( + g, gd, gdd, a, ad, (zero, dn*v, db*v)) + if arg == 2: + return rotated_centroid_frame_dash_dcoeff_vjp2( + g, gd, gdd, a, ad, (zero, dn*v, db*v)) + if arg == 3: + return None + if arg == 4: + return rotated_centroid_frame_dash_dcoeff_vjp4( + g, gd, gdd, a, ad, (zero, dn*v, db*v)) + if arg == 5: + return rotated_centroid_frame_dash_dcoeff_vjp5( + g, gd, gdd, a, ad, (zero, dn*v, db*v)) + class FrameRotation(Optimizable): @@ -363,7 +440,7 @@ def rotated_centroid_frame(gamma, gammadash, alpha): lambda gamma, gammadash, alpha, v: vjp( lambda gd: rotated_centroid_frame(gamma, gd, alpha), gammadash)[1](v)[0]) -rotated_centroid_frame_dcoeff_vjp2 = jit( +rotated_centroid_frame_dcoeff_vjp3 = jit( lambda gamma, gammadash, alpha, v: vjp( lambda a: rotated_centroid_frame(gamma, gammadash, a), alpha)[1](v)[0]) @@ -379,11 +456,11 @@ def rotated_centroid_frame(gamma, gammadash, alpha): lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda gdd: rotated_centroid_frame_dash(gamma, gammadash, gdd, alpha, alphadash), gammadashdash)[1](v)[0]) -rotated_centroid_frame_dash_dcoeff_vjp3 = jit( +rotated_centroid_frame_dash_dcoeff_vjp4 = jit( lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda a: rotated_centroid_frame_dash(gamma, gammadash, gammadashdash, a, alphadash), alpha)[1](v)[0]) -rotated_centroid_frame_dash_dcoeff_vjp4 = jit( +rotated_centroid_frame_dash_dcoeff_vjp5 = jit( lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda ad: rotated_centroid_frame_dash(gamma, gammadash, gammadashdash, alpha, ad), alphadash)[1](v)[0]) diff --git a/tests/geo/test_finitebuild.py b/tests/geo/test_finitebuild.py index 6c389d45f..84bfe467b 100644 --- a/tests/geo/test_finitebuild.py +++ b/tests/geo/test_finitebuild.py @@ -3,8 +3,8 @@ from simsopt.field.biotsavart import BiotSavart from simsopt.field.coil import Coil, apply_symmetries_to_curves, apply_symmetries_to_currents from simsopt.geo.curveobjectives import CurveLength, CurveCurveDistance -from simsopt.geo.finitebuild import CurveFilament, FilamentRotation, \ - create_multifilament_grid, ZeroRotation +from simsopt.geo import CurveFilament, FrameRotation, \ + create_multifilament_grid, ZeroRotation, FramedCurveCentroid, FramedCurveFrenet from simsopt.geo.qfmsurface import QfmSurface from simsopt.objectives.fluxobjective import SquaredFlux from simsopt.objectives.utilities import QuadraticPenalty @@ -16,25 +16,31 @@ class MultifilamentTesting(unittest.TestCase): def test_multifilament_gammadash(self): - for order in [None, 1]: - with self.subTest(order=order): - self.subtest_multifilament_gammadash(order) + for centroid in [True,False]: + for order in [None, 1]: + with self.subTest(order=order): + self.subtest_multifilament_gammadash(order,centroid) - def subtest_multifilament_gammadash(self, order): + def subtest_multifilament_gammadash(self, order, centroid): assert order in [1, None] - curves, currents, ma = get_ncsx_data(Nt_coils=6, ppp=80) + curves, currents, ma = get_ncsx_data(Nt_coils=6, ppp=120) c = curves[0] if order == 1: - rotation = FilamentRotation(c.quadpoints, order) + rotation = FrameRotation(c.quadpoints, order) rotation.x = np.array([0, 0.1, 0.3]) - rotationShared = FilamentRotation(curves[0].quadpoints, order, dofs=rotation.dofs) + rotationShared = FrameRotation(curves[0].quadpoints, order, dofs=rotation.dofs) assert np.allclose(rotation.x, rotationShared.x) assert np.allclose(rotation.alpha(c.quadpoints), rotationShared.alpha(c.quadpoints)) else: rotation = ZeroRotation(c.quadpoints) - c = CurveFilament(c, 0.01, 0.01, rotation) + if centroid: + framedcurve = FramedCurveCentroid(c, rotation) + else: + framedcurve = FramedCurveFrenet(c, rotation) + + c = CurveFilament(framedcurve, 0.01, 0.01) g = c.gamma() gd = c.gammadash() idx = 16 @@ -51,22 +57,28 @@ def subtest_multifilament_gammadash(self, order): def test_multifilament_coefficient_derivative(self): for order in [None, 1]: - with self.subTest(order=order): - self.subtest_multifilament_coefficient_derivative(order) + for centroid in [True,False]: + with self.subTest(order=order): + self.subtest_multifilament_coefficient_derivative(order,centroid) - def subtest_multifilament_coefficient_derivative(self, order): + def subtest_multifilament_coefficient_derivative(self, order, centroid): assert order in [1, None] curves, currents, ma = get_ncsx_data(Nt_coils=4, ppp=10) c = curves[0] if order == 1: - rotation = FilamentRotation(c.quadpoints, order) + rotation = FrameRotation(c.quadpoints, order) rotation.x = np.array([0, 0.1, 0.3]) else: rotation = ZeroRotation(c.quadpoints) - c = CurveFilament(c, 0.02, 0.02, rotation) + if centroid: + framedcurve = FramedCurveCentroid(c, rotation) + else: + framedcurve = FramedCurveFrenet(c, rotation) + + c = CurveFilament(framedcurve, 0.02, 0.02) dofs = c.x @@ -123,26 +135,27 @@ def check(fils, c, numfilaments_n, numfilaments_b): # check that the coil pack is centered around the underlying curve assert np.linalg.norm(np.mean([f.gamma() for f in fils], axis=0)-c.gamma()) < 1e-13 - numfilaments_n = 2 - numfilaments_b = 3 - fils = create_multifilament_grid( - c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, - rotation_order=None, rotation_scaling=None) - check(fils, c, numfilaments_n, numfilaments_b) - - numfilaments_n = 3 - numfilaments_b = 2 - fils = create_multifilament_grid( - c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, - rotation_order=None, rotation_scaling=None) - check(fils, c, numfilaments_n, numfilaments_b) - - fils = create_multifilament_grid( - c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, - rotation_order=3, rotation_scaling=None) - xr = fils[0].rotation.x - fils[0].rotation.x = xr + 1e-2*np.random.standard_normal(size=xr.shape) - check(fils, c, numfilaments_n, numfilaments_b) + for frame in ['centroid','frenet']: + numfilaments_n = 2 + numfilaments_b = 3 + fils = create_multifilament_grid( + c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, + rotation_order=None, rotation_scaling=None,frame=frame) + check(fils, c, numfilaments_n, numfilaments_b) + + numfilaments_n = 3 + numfilaments_b = 2 + fils = create_multifilament_grid( + c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, + rotation_order=None, rotation_scaling=None,frame=frame) + check(fils, c, numfilaments_n, numfilaments_b) + + fils = create_multifilament_grid( + c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, + rotation_order=3, rotation_scaling=None,frame=frame) + xr = fils[0].rotation.x + fils[0].rotation.x = xr + 1e-2*np.random.standard_normal(size=xr.shape) + check(fils, c, numfilaments_n, numfilaments_b) def test_biotsavart_with_symmetries(self): """ @@ -152,46 +165,49 @@ def test_biotsavart_with_symmetries(self): """ np.random.seed(1) base_curves, base_currents, ma = get_ncsx_data(Nt_coils=5) - base_curves_finite_build = sum( - [create_multifilament_grid(c, 2, 2, 0.01, 0.01, rotation_order=1) for c in base_curves], []) - base_currents_finite_build = sum([[c]*4 for c in base_currents], []) - - nfp = 3 - - curves = apply_symmetries_to_curves(base_curves, nfp, True) - curves_fb = apply_symmetries_to_curves(base_curves_finite_build, nfp, True) - currents_fb = apply_symmetries_to_currents(base_currents_finite_build, nfp, True) - - coils_fb = [Coil(c, curr) for (c, curr) in zip(curves_fb, currents_fb)] - - bs = BiotSavart(coils_fb) - s = get_surface("SurfaceXYZFourier", True) - s.fit_to_curve(ma, 0.1) - Jf = SquaredFlux(s, bs) - Jls = [CurveLength(c) for c in base_curves] - Jdist = CurveCurveDistance(curves, 0.5) - LENGTH_PEN = 1e-2 - DIST_PEN = 1e-2 - JF = Jf \ - + LENGTH_PEN * sum(QuadraticPenalty(Jls[i], Jls[i].J()) for i in range(len(base_curves))) \ - + DIST_PEN * Jdist - - def fun(dofs, grad=True): - JF.x = dofs - return (JF.J(), JF.dJ()) if grad else JF.J() - - dofs = JF.x - dofs += 1e-2 * np.random.standard_normal(size=dofs.shape) - np.random.seed(1) - h = np.random.uniform(size=dofs.shape) - J0, dJ0 = fun(dofs) - dJh = sum(dJ0 * h) - err = 1e6 - for i in range(10, 15): - eps = 0.5**i - J1 = fun(dofs + eps*h, grad=False) - J2 = fun(dofs - eps*h, grad=False) - err_new = abs((J1-J2)/(2*eps) - dJh) - assert err_new < 0.55**2 * err - err = err_new - print("err", err) + + for frame in ['centroid','frenet']: + + base_curves_finite_build = sum( + [create_multifilament_grid(c, 2, 2, 0.01, 0.01, rotation_order=1,frame=frame) for c in base_curves], []) + base_currents_finite_build = sum([[c]*4 for c in base_currents], []) + + nfp = 3 + + curves = apply_symmetries_to_curves(base_curves, nfp, True) + curves_fb = apply_symmetries_to_curves(base_curves_finite_build, nfp, True) + currents_fb = apply_symmetries_to_currents(base_currents_finite_build, nfp, True) + + coils_fb = [Coil(c, curr) for (c, curr) in zip(curves_fb, currents_fb)] + + bs = BiotSavart(coils_fb) + s = get_surface("SurfaceXYZFourier", True) + s.fit_to_curve(ma, 0.1) + Jf = SquaredFlux(s, bs) + Jls = [CurveLength(c) for c in base_curves] + Jdist = CurveCurveDistance(curves, 0.5) + LENGTH_PEN = 1e-2 + DIST_PEN = 1e-2 + JF = Jf \ + + LENGTH_PEN * sum(QuadraticPenalty(Jls[i], Jls[i].J()) for i in range(len(base_curves))) \ + + DIST_PEN * Jdist + + def fun(dofs, grad=True): + JF.x = dofs + return (JF.J(), JF.dJ()) if grad else JF.J() + + dofs = JF.x + dofs += 1e-2 * np.random.standard_normal(size=dofs.shape) + np.random.seed(1) + h = np.random.uniform(size=dofs.shape) + J0, dJ0 = fun(dofs) + dJh = sum(dJ0 * h) + err = 1e6 + for i in range(10, 15): + eps = 0.5**i + J1 = fun(dofs + eps*h, grad=False) + J2 = fun(dofs - eps*h, grad=False) + err_new = abs((J1-J2)/(2*eps) - dJh) + assert err_new < 0.55**2 * err + err = err_new + print("err", err) From 72632aeb895a4474b3a4dc262f0386caef9b7ff6 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Tue, 8 Aug 2023 09:24:43 -0400 Subject: [PATCH 15/61] Autopep. --- src/simsopt/geo/framedcurve.py | 2 +- tests/geo/test_finitebuild.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index b20237717..fce7db98d 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -269,7 +269,7 @@ def frame_binormal_curvature(self): def rotated_frame(self): return rotated_centroid_frame(self.curve.gamma(), self.curve.gammadash(), - self.rotation.alpha(self.curve.quadpoints)) + self.rotation.alpha(self.curve.quadpoints)) def rotated_frame_dash(self): return rotated_centroid_frame_dash( diff --git a/tests/geo/test_finitebuild.py b/tests/geo/test_finitebuild.py index 84bfe467b..d1a90ff14 100644 --- a/tests/geo/test_finitebuild.py +++ b/tests/geo/test_finitebuild.py @@ -16,10 +16,10 @@ class MultifilamentTesting(unittest.TestCase): def test_multifilament_gammadash(self): - for centroid in [True,False]: + for centroid in [True, False]: for order in [None, 1]: with self.subTest(order=order): - self.subtest_multifilament_gammadash(order,centroid) + self.subtest_multifilament_gammadash(order, centroid) def subtest_multifilament_gammadash(self, order, centroid): assert order in [1, None] @@ -57,9 +57,9 @@ def subtest_multifilament_gammadash(self, order, centroid): def test_multifilament_coefficient_derivative(self): for order in [None, 1]: - for centroid in [True,False]: + for centroid in [True, False]: with self.subTest(order=order): - self.subtest_multifilament_coefficient_derivative(order,centroid) + self.subtest_multifilament_coefficient_derivative(order, centroid) def subtest_multifilament_coefficient_derivative(self, order, centroid): assert order in [1, None] @@ -135,24 +135,24 @@ def check(fils, c, numfilaments_n, numfilaments_b): # check that the coil pack is centered around the underlying curve assert np.linalg.norm(np.mean([f.gamma() for f in fils], axis=0)-c.gamma()) < 1e-13 - for frame in ['centroid','frenet']: + for frame in ['centroid', 'frenet']: numfilaments_n = 2 numfilaments_b = 3 fils = create_multifilament_grid( c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, - rotation_order=None, rotation_scaling=None,frame=frame) + rotation_order=None, rotation_scaling=None, frame=frame) check(fils, c, numfilaments_n, numfilaments_b) numfilaments_n = 3 numfilaments_b = 2 fils = create_multifilament_grid( c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, - rotation_order=None, rotation_scaling=None,frame=frame) + rotation_order=None, rotation_scaling=None, frame=frame) check(fils, c, numfilaments_n, numfilaments_b) fils = create_multifilament_grid( c, numfilaments_n, numfilaments_b, gapsize_n, gapsize_b, - rotation_order=3, rotation_scaling=None,frame=frame) + rotation_order=3, rotation_scaling=None, frame=frame) xr = fils[0].rotation.x fils[0].rotation.x = xr + 1e-2*np.random.standard_normal(size=xr.shape) check(fils, c, numfilaments_n, numfilaments_b) @@ -166,10 +166,10 @@ def test_biotsavart_with_symmetries(self): np.random.seed(1) base_curves, base_currents, ma = get_ncsx_data(Nt_coils=5) - for frame in ['centroid','frenet']: + for frame in ['centroid', 'frenet']: base_curves_finite_build = sum( - [create_multifilament_grid(c, 2, 2, 0.01, 0.01, rotation_order=1,frame=frame) for c in base_curves], []) + [create_multifilament_grid(c, 2, 2, 0.01, 0.01, rotation_order=1, frame=frame) for c in base_curves], []) base_currents_finite_build = sum([[c]*4 for c in base_currents], []) nfp = 3 From 9926c781b2b3ff20fdc105ee4f9e53a4530c3fe1 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Tue, 8 Aug 2023 10:54:54 -0400 Subject: [PATCH 16/61] Added unit tests for strains. --- src/simsopt/geo/framedcurve.py | 63 +++++++++++++++++++++++++++++++++- tests/geo/test_finitebuild.py | 1 + 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index fce7db98d..1bd0d08c7 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -240,7 +240,7 @@ def __init__(self, curve, rotation=None): lambda g: self.torsion(gamma, g, gammadashdash, alpha, alphadash), gammadash)[1](v)[0]) self.torsiongrad_vjp2 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, g, alpha, alphadash), gammadashdash)[1](v)[0]) - self.torsiongrad_vjp3 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + self.torsiongrad_vjp4 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, gammadashdash, g, alphadash), alpha)[1](v)[0]) self.torsiongrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, gammadashdash, alpha, g), alphadash)[1](v)[0]) @@ -248,6 +248,19 @@ def __init__(self, curve, rotation=None): self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( gamma, gammadash, gammadashdash, alpha, alphadash)) + self.binorm = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( + gamma, gammadash, gammadashdash, alpha, alphadash)) + self.binormgrad_vjp0 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(g, gammadash, gammadashdash, alpha, alphadash), gamma)[1](v)[0]) + self.binormgrad_vjp1 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, g, gammadashdash, alpha, alphadash), gammadash)[1](v)[0]) + self.binormgrad_vjp2 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, gammadash, g, alpha, alphadash), gammadashdash)[1](v)[0]) + self.binormgrad_vjp4 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, gammadash, gammadashdash, g, alphadash), alpha)[1](v)[0]) + self.binormgrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( + lambda g: self.binorm(gamma, gammadash, gammadashdash, alpha, g), alphadash)[1](v)[0]) + def frame_torsion(self): """Exports frame torsion along a curve""" gamma = self.curve.gamma() @@ -346,6 +359,54 @@ def rotated_frame_dash_dcoeff_vjp(self, v, dn, db, arg=0): return rotated_centroid_frame_dash_dcoeff_vjp5( g, gd, gdd, a, ad, (zero, dn*v, db*v)) + def dframe_binormal_curvature_by_dcoeff_vjp(self, v): + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + + grad0 = self.binormgrad_vjp0(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + grad1 = self.binormgrad_vjp1(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + grad2 = self.binormgrad_vjp2(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + grad4 = self.binormgrad_vjp4(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + grad5 = self.binormgrad_vjp5(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + + return self.curve.dgamma_by_dcoeff_vjp(grad0) \ + + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ + + self.curve.dgammadashdash_by_dcoeff_vjp(grad2) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ + + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + + def dframe_torsion_by_dcoeff_vjp(self, v): + gamma = self.curve.gamma() + d1gamma = self.curve.gammadash() + d2gamma = self.curve.gammadashdash() + alpha = self.rotation.alpha(self.curve.quadpoints) + alphadash = self.rotation.alphadash(self.curve.quadpoints) + + grad0 = self.torsiongrad_vjp0(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + grad1 = self.torsiongrad_vjp1(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + grad2 = self.torsiongrad_vjp2(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + grad4 = self.torsiongrad_vjp4(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + grad5 = self.torsiongrad_vjp5(gamma, d1gamma, d2gamma, + alpha, alphadash, v) + + return self.curve.dgamma_by_dcoeff_vjp(grad0) \ + + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ + + self.curve.dgammadashdash_by_dcoeff_vjp(grad2) \ + + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ + + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) + class FrameRotation(Optimizable): diff --git a/tests/geo/test_finitebuild.py b/tests/geo/test_finitebuild.py index d1a90ff14..ff1bc8d74 100644 --- a/tests/geo/test_finitebuild.py +++ b/tests/geo/test_finitebuild.py @@ -9,6 +9,7 @@ from simsopt.objectives.fluxobjective import SquaredFlux from simsopt.objectives.utilities import QuadraticPenalty from simsopt.configs.zoo import get_ncsx_data +from simsopt.geo.strain_optimization_classes import LPBinormalCurvatureStrainPenalty, LPTorsionalStrainPenalty import numpy as np From acd058f7892b772fb529c74b7d7c824ed8094bb9 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Tue, 8 Aug 2023 10:55:04 -0400 Subject: [PATCH 17/61] Added unit tests for strains. --- tests/geo/test_strainopt.py | 99 +++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 tests/geo/test_strainopt.py diff --git a/tests/geo/test_strainopt.py b/tests/geo/test_strainopt.py new file mode 100644 index 000000000..1c6d0098e --- /dev/null +++ b/tests/geo/test_strainopt.py @@ -0,0 +1,99 @@ +import unittest +from simsopt.geo import FrameRotation, ZeroRotation, FramedCurveCentroid, FramedCurveFrenet +from simsopt.configs.zoo import get_ncsx_data +from simsopt.geo.strain_optimization_classes import LPBinormalCurvatureStrainPenalty, LPTorsionalStrainPenalty +import numpy as np + + +class StrainOptTesting(unittest.TestCase): + + def test_torsion(self): + for centroid in [True, False]: + for order in [None, 1]: + with self.subTest(order=order): + self.subtest_torsion(order, centroid) + + def test_binormal_curvature(self): + for centroid in [True, False]: + for order in [None, 1]: + with self.subTest(order=order): + self.subtest_binormal_curvature(order, centroid) + + def subtest_binormal_curvature(self, order, centroid): + assert order in [1, None] + curves, currents, ma = get_ncsx_data(Nt_coils=6, ppp=120) + c = curves[0] + + if order == 1: + rotation = FrameRotation(c.quadpoints, order) + rotation.x = np.array([0, 0.1, 0.3]) + rotationShared = FrameRotation(curves[0].quadpoints, order, dofs=rotation.dofs) + assert np.allclose(rotation.x, rotationShared.x) + assert np.allclose(rotation.alpha(c.quadpoints), rotationShared.alpha(c.quadpoints)) + else: + rotation = ZeroRotation(c.quadpoints) + + if centroid: + framedcurve = FramedCurveCentroid(c, rotation) + else: + framedcurve = FramedCurveFrenet(c, rotation) + + J = LPBinormalCurvatureStrainPenalty(framedcurve, width=1e-3, p=2, threshold=1e-4) + + if (not (not centroid and order is None)): + dofs = J.x + + np.random.seed(1) + h = np.random.standard_normal(size=dofs.shape) + df = np.sum(J.dJ()*h) + + errf_old = 1e10 + for i in range(9, 14): + eps = 0.5**i + J.x = dofs + eps*h + f1 = J.J() + J.x = dofs - eps*h + f2 = J.J() + errf = np.abs((f1-f2)/(2*eps) - df) + errf_old = errf + else: + # Binormal curvature vanishes in Frenet frame + assert J.J() < 1e-12 + + def subtest_torsion(self, order, centroid): + assert order in [1, None] + curves, currents, ma = get_ncsx_data(Nt_coils=6, ppp=120) + c = curves[0] + + if order == 1: + rotation = FrameRotation(c.quadpoints, order) + rotation.x = np.array([0, 0.1, 0.3]) + rotationShared = FrameRotation(curves[0].quadpoints, order, dofs=rotation.dofs) + assert np.allclose(rotation.x, rotationShared.x) + assert np.allclose(rotation.alpha(c.quadpoints), rotationShared.alpha(c.quadpoints)) + else: + rotation = ZeroRotation(c.quadpoints) + + if centroid: + framedcurve = FramedCurveCentroid(c, rotation) + else: + framedcurve = FramedCurveFrenet(c, rotation) + + J = LPTorsionalStrainPenalty(framedcurve, width=1e-3, p=2, threshold=1e-4) + + dofs = J.x + + np.random.seed(1) + h = np.random.standard_normal(size=dofs.shape) + df = np.sum(J.dJ()*h) + + errf_old = 1e10 + for i in range(9, 14): + eps = 0.5**i + J.x = dofs + eps*h + f1 = J.J() + J.x = dofs - eps*h + f2 = J.J() + errf = np.abs((f1-f2)/(2*eps) - df) + errf_old = errf + From 51674cf79a248665d4bd1d239968af44dc6d807c Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Tue, 8 Aug 2023 11:39:54 -0400 Subject: [PATCH 18/61] Added simple example. --- .../strain_optimization_simple.py | 61 +++++++++++++++++++ ...tion_classes.py => strain_optimization.py} | 2 + tests/geo/test_strainopt.py | 2 +- 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 examples/2_Intermediate/strain_optimization_simple.py rename src/simsopt/geo/{strain_optimization_classes.py => strain_optimization.py} (98%) diff --git a/examples/2_Intermediate/strain_optimization_simple.py b/examples/2_Intermediate/strain_optimization_simple.py new file mode 100644 index 000000000..5f22b6009 --- /dev/null +++ b/examples/2_Intermediate/strain_optimization_simple.py @@ -0,0 +1,61 @@ +""" +This script performs an optimization of the HTS tape winding angle +with respect to binormal curvature and torsional strain cost functions. +The orientation of the tape is defined wrt the Frener-Serret Frame +""" + +import numpy as np +import os +import numpy as np +from scipy.optimize import minimize +from simsopt.geo import StrainOpt, LPTorsionalStrainPenalty, LPBinormalCurvatureStrainPenalty +from simsopt.geo import FrameRotation, FramedCurveFrenet, CurveXYZFourier +from simsopt.field import load_coils_from_makegrid_file +from simsopt.configs import get_hsx_data +import matplotlib.pyplot as plt + +ci = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] +MAXITER = 50 if ci else 400 + +curves, currents, ma = get_hsx_data(Nt_coils=10, ppp=10) +curve = curves[1] +scale_factor = 0.1 +curve_scaled = CurveXYZFourier(curve.quadpoints, curve.order) +curve_scaled.x = curve.x * scale_factor # scale coil to magnify the strains +rot_order = 10 # order of the Fourier expression for the rotation of the filament pack +width = 1e-3 # tape width + +curve_scaled.fix_all() # fix curve DOFs -> only optimize winding angle +rotation = FrameRotation(curve_scaled.quadpoints, rot_order) + +framedcurve = FramedCurveFrenet(curve_scaled, rotation) + +tor_threshold = 0.02 # Threshold for strain parameters +cur_threshold = 0.02 + +Jtor = LPTorsionalStrainPenalty(framedcurve, p=2, threshold=tor_threshold) +Jbin = LPBinormalCurvatureStrainPenalty(framedcurve, p=2, threshold=cur_threshold) + +strain = StrainOpt(framedcurve, width) +JF = Jtor + Jbin + + +def fun(dofs): + JF.x = dofs + J = JF.J() + grad = JF.dJ() + outstr = f"Max torsional strain={np.max(strain.torsional_strain()):.1e}, Max curvature strain={np.max(strain.binormal_curvature_strain()):.1e}" + print(outstr) + return J, grad + + +f = fun +dofs = JF.x + +print(""" +################################################################################ +### Run the optimisation ####################################################### +################################################################################ +""") +res = minimize(fun, dofs, jac=True, method='L-BFGS-B', + options={'maxiter': MAXITER, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) diff --git a/src/simsopt/geo/strain_optimization_classes.py b/src/simsopt/geo/strain_optimization.py similarity index 98% rename from src/simsopt/geo/strain_optimization_classes.py rename to src/simsopt/geo/strain_optimization.py index 65ebc6954..e3499bc1c 100644 --- a/src/simsopt/geo/strain_optimization_classes.py +++ b/src/simsopt/geo/strain_optimization.py @@ -8,6 +8,8 @@ from simsopt._core.derivative import derivative_dec from simsopt.geo.curveobjectives import Lp_torsion_pure +__all__ = ['LPBinormalCurvatureStrainPenalty', 'LPTorsionalStrainPenalty', 'StrainOpt'] + class LPBinormalCurvatureStrainPenalty(Optimizable): r""" diff --git a/tests/geo/test_strainopt.py b/tests/geo/test_strainopt.py index 1c6d0098e..4fb7ef147 100644 --- a/tests/geo/test_strainopt.py +++ b/tests/geo/test_strainopt.py @@ -1,7 +1,7 @@ import unittest from simsopt.geo import FrameRotation, ZeroRotation, FramedCurveCentroid, FramedCurveFrenet from simsopt.configs.zoo import get_ncsx_data -from simsopt.geo.strain_optimization_classes import LPBinormalCurvatureStrainPenalty, LPTorsionalStrainPenalty +from simsopt.geo.strain_optimization import LPBinormalCurvatureStrainPenalty, LPTorsionalStrainPenalty import numpy as np From e9f154fa4a2d823587654e8780a1a0c7f72bddf6 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Tue, 8 Aug 2023 11:41:54 -0400 Subject: [PATCH 19/61] Removed old example. --- .../3_Advanced/strain_optimization_script.py | 179 ------------------ src/simsopt/geo/__init__.py | 1 + 2 files changed, 1 insertion(+), 179 deletions(-) delete mode 100644 examples/3_Advanced/strain_optimization_script.py diff --git a/examples/3_Advanced/strain_optimization_script.py b/examples/3_Advanced/strain_optimization_script.py deleted file mode 100644 index 2785c703c..000000000 --- a/examples/3_Advanced/strain_optimization_script.py +++ /dev/null @@ -1,179 +0,0 @@ -""" -This script performs a stage two coil optimization -with additional terms for torsion and binormal curvature in the cost function. -We use a multifilament approach that initializes the coils using the Frener-Serret Frame -""" - -import os -from pathlib import Path -import numpy as np -from scipy.optimize import minimize -from simsopt.field import BiotSavart -from simsopt.field import Current, Coil, apply_symmetries_to_curves, apply_symmetries_to_currents -from simsopt.geo import curves_to_vtk, create_equally_spaced_curves -from simsopt.geo import CurveLength, CurveCurveDistance, CurveSurfaceDistance -from simsopt.geo import SurfaceRZFourier -from simsopt.geo import ArclengthVariation -from simsopt.objectives import SquaredFlux -from simsopt.objectives import QuadraticPenalty -from simsopt.geo.strain_optimization_classes import create_multifilament_grid_frenet, StrainOpt -# from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb - - -# Number of unique coil shapes, i.e. the number of coils per half field period: -# (Since the configuration has nfp = 2, multiply by 4 to get the total number of coils.) -ncoils = 4 - -# Major radius for the initial circular coils: -R0 = 1.00 - -# Minor radius for the initial circular coils: -R1 = 0.40 - -# Number of Fourier modes describing each Cartesian component of each coil: -order = 5 - -# Set up the winding pack -numfilaments_n = 1 # number of filaments in normal direction -numfilaments_b = 1 # number of filaments in bi-normal direction -gapsize_n = 0.02 # gap between filaments in normal direction -gapsize_b = 0.03 # gap between filaments in bi-normal direction -rot_order = 5 # order of the Fourier expression for the rotation of the filament pack - -scale = 1 -width = 12 - -# Weight on the curve length penalty in the objective function: -LENGTH_PEN = 1e-2 - -# Threshold and weight for the coil-to-coil distance penalty in the objective function: -CC_THRESHOLD = 0.1 -CC_WEIGHT = 1e-4 # 2e-1 - -# Threshold and weight for the coil-to-surface distance penalty in the objective function: -CS_THRESHOLD = 0.1 -CS_WEIGHT = 0 # 2e-2 - -# weight for penalty on winding pack strain -STRAIN_WEIGHT = 1e-13 # 3e-7 -# weight for arclength error. -# Arclength parametrization is crucial otherwise the formulas for torsion and curvature are wrong -ARCLENGTH_WEIGHT = 1 - - -# Number of iterations to perform: -ci = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] -MAXITER = 500 if ci else 500 - -####################################################### -# End of input parameters. -####################################################### - -# File for the desired boundary magnetic surface: -TEST_DIR = (Path(__file__).parent / ".." / ".." / - "tests" / "test_files").resolve() -filename = TEST_DIR / 'input.LandremanPaul2021_QA' - -# Directory for output -OUT_DIR = "./output/" -os.makedirs(OUT_DIR, exist_ok=True) - -config_str = f"{ncoils}_coils_rot_order_{rot_order}_nfn_{numfilaments_n}_nfb_{numfilaments_b}\ - _strain_weight_{STRAIN_WEIGHT}" - -# Initialize the boundary magnetic surface: -nphi = 64 -ntheta = 64 -s = SurfaceRZFourier.from_vmec_input( - filename, range="half period", nphi=nphi, ntheta=ntheta) - -nfil = numfilaments_n * numfilaments_b -base_curves = create_equally_spaced_curves( - ncoils, s.nfp, stellsym=True, R0=R0, R1=R1, order=order) -base_currents = [] -for i in range(ncoils): - curr = Current(1.) - # since the target field is zero, one possible solution is just to set all - # currents to 0. to avoid the minimizer finding that solution, we fix one - # of the currents - if i == 0: - curr.fix_all() - base_currents.append(curr * (5e4/nfil)) - -# use sum here to concatenate lists -base_curves_finite_build = sum([ - create_multifilament_grid_frenet(c, numfilaments_n, numfilaments_b, gapsize_n, - gapsize_b, rotation_order=rot_order) for c in base_curves], []) -base_currents_finite_build = sum([[c]*nfil for c in base_currents], []) - -# apply stellarator and rotation symmetries -curves_fb = apply_symmetries_to_curves(base_curves_finite_build, s.nfp, True) -currents_fb = apply_symmetries_to_currents( - base_currents_finite_build, s.nfp, True) -# also apply symmetries to the underlying base curves, as we use those in the -# curve-curve distance penalty -curves = apply_symmetries_to_curves(base_curves, s.nfp, True) - -coils_fb = [Coil(c, curr) for (c, curr) in zip(curves_fb, currents_fb)] -coils_exp = [Coil(c, curr) for (c, curr) in zip( - base_curves_finite_build, base_currents_finite_build)] -bs = BiotSavart(coils_fb) -bs.set_points(s.gamma().reshape((-1, 3))) -curves_to_vtk(curves, OUT_DIR + "curves_init") -curves_to_vtk(curves_fb, OUT_DIR + f"curves_init_fb_{config_str}") - -pointData = {"B_N": np.sum(bs.B().reshape( - (nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} -s.to_vtk(OUT_DIR + f"surf_init_fb_{config_str}", extra_data=pointData) - -# Define the individual terms of the objective function: -Jf = SquaredFlux(s, bs) -Jls = [CurveLength(c) for c in base_curves] -Jccdist = CurveCurveDistance(curves, CC_THRESHOLD, num_basecurves=ncoils) -Jcsdist = CurveSurfaceDistance(curves, s, CS_THRESHOLD) -Jarc = [ArclengthVariation(c) for c in base_curves] -Jstrain_list = [StrainOpt(c, width=width, scale=scale) - for c in base_curves_finite_build] -Jstrain = sum(QuadraticPenalty(Jstrain_list[i], Jstrain_list[i].J( -)) for i in range(len(base_curves_finite_build))) - - -# Assemble the objective function -JF = Jf \ - + STRAIN_WEIGHT * Jstrain \ - + LENGTH_PEN * sum(QuadraticPenalty(Jls[i], Jls[i].J()) for i in range(len(base_curves))) \ - + CC_WEIGHT * Jccdist \ - + CS_WEIGHT * Jcsdist \ - + ARCLENGTH_WEIGHT * sum(Jarc) \ - - - -def fun(dofs): - JF.x = dofs - J = JF.J() - grad = JF.dJ() - # cl_string = ", ".join([f"{J.J():.3f}" for J in Jls]) - # mean_AbsB = np.mean(bs.AbsB()) - # jf = Jf.J() - # strain_string = Jstrain.J() - # print(f"J={J:.3e}, Jflux={jf:.3e}, sqrt(Jflux)/Mean(|B|)={np.sqrt(jf)/mean_AbsB:.3e}, CoilLengths=[{cl_string}], {strain_string:.3e}, ||∇J||={np.linalg.norm(grad):.3e}") - return 1e-4*J, 1e-4*grad - - -f = fun -dofs = JF.x - - -print(""" -################################################################################ -### Run the optimisation ####################################################### -################################################################################ -""") -res = minimize(fun, dofs, jac=True, method='L-BFGS-B', - options={'maxiter': MAXITER, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) -curves_to_vtk(curves_fb, OUT_DIR + f"curves_opt_fb_{config_str}") -curves_to_vtk(base_curves_finite_build, OUT_DIR + - f"curves_opt_fb_hfp_{config_str}") -pointData = {"B_N": np.sum(bs.B().reshape( - (nphi, ntheta, 3)) * s.unitnormal(), axis=2)[:, :, None]} -s.to_vtk(OUT_DIR + f"surf_opt_fb_{config_str}", extra_data=pointData) diff --git a/src/simsopt/geo/__init__.py b/src/simsopt/geo/__init__.py index 12eca158b..a3d1dd1d6 100644 --- a/src/simsopt/geo/__init__.py +++ b/src/simsopt/geo/__init__.py @@ -21,6 +21,7 @@ from .surfacerzfourier import * from .surfacexyzfourier import * from .surfacexyztensorfourier import * +from .strain_optimization import * __all__ = (curve.__all__ + curvehelical.__all__ + curverzfourier.__all__ + curvexyzfourier.__all__ + From bff6c2c3cb3d9355742d681dfb9af15e90c34486 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Tue, 8 Aug 2023 11:44:23 -0400 Subject: [PATCH 20/61] Small cleanup. --- src/simsopt/geo/framedcurve.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index 1bd0d08c7..fdf8bda69 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -79,9 +79,6 @@ def __init__(self, curve, rotation=None): self.torsiongrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, g), alphadash)[1](v)[0]) - self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash: binormal_curvature_pure_frenet( - gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash)) - def rotated_frame(self): return rotated_frenet_frame(self.curve.gamma(), self.curve.gammadash(), self.curve.gammadashdash(), self.rotation.alpha(self.curve.quadpoints)) @@ -108,7 +105,7 @@ def frame_binormal_curvature(self): d3gamma = self.curve.gammadashdashdash() alpha = self.rotation.alpha(self.curve.quadpoints) alphadash = self.rotation.alphadash(self.curve.quadpoints) - return self.binormal_curvature(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash) + return self.binorm(gamma, d1gamma, d2gamma, d3gamma, alpha, alphadash) def dframe_torsion_by_dcoeff_vjp(self, v): gamma = self.curve.gamma() @@ -231,9 +228,9 @@ class FramedCurveCentroid(FramedCurve): def __init__(self, curve, rotation=None): FramedCurve.__init__(self, curve, rotation) + self.torsion = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: torsion_pure_centroid( gamma, gammadash, gammadashdash, alpha, alphadash)) - self.torsiongrad_vjp0 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(g, gammadash, gammadashdash, alpha, alphadash), gamma)[1](v)[0]) self.torsiongrad_vjp1 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( @@ -245,9 +242,6 @@ def __init__(self, curve, rotation=None): self.torsiongrad_vjp5 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( lambda g: self.torsion(gamma, gammadash, gammadashdash, alpha, g), alphadash)[1](v)[0]) - self.binormal_curvature = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( - gamma, gammadash, gammadashdash, alpha, alphadash)) - self.binorm = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: binormal_curvature_pure_centroid( gamma, gammadash, gammadashdash, alpha, alphadash)) self.binormgrad_vjp0 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( @@ -278,7 +272,7 @@ def frame_binormal_curvature(self): d3gamma = self.curve.gammadashdashdash() alpha = self.rotation.alpha(self.curve.quadpoints) alphadash = self.rotation.alphadash(self.curve.quadpoints) - return self.binormal_curvature(gamma, d1gamma, d2gamma, alpha, alphadash) + return self.binorm(gamma, d1gamma, d2gamma, alpha, alphadash) def rotated_frame(self): return rotated_centroid_frame(self.curve.gamma(), self.curve.gammadash(), From ee2525646d55330200caeeded442b2d4c6fb8bf0 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Tue, 8 Aug 2023 12:32:27 -0400 Subject: [PATCH 21/61] Fix to test. --- tests/geo/test_finitebuild.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/geo/test_finitebuild.py b/tests/geo/test_finitebuild.py index ff1bc8d74..d1a90ff14 100644 --- a/tests/geo/test_finitebuild.py +++ b/tests/geo/test_finitebuild.py @@ -9,7 +9,6 @@ from simsopt.objectives.fluxobjective import SquaredFlux from simsopt.objectives.utilities import QuadraticPenalty from simsopt.configs.zoo import get_ncsx_data -from simsopt.geo.strain_optimization_classes import LPBinormalCurvatureStrainPenalty, LPTorsionalStrainPenalty import numpy as np From 7ebe834ba9e12102b6511e3aeec9a675730c73d1 Mon Sep 17 00:00:00 2001 From: phuslage <48801106+phuslage@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:04:59 +0200 Subject: [PATCH 22/61] Delete biotsavart_mod.py --- src/simsopt/field/biotsavart_mod.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/simsopt/field/biotsavart_mod.py diff --git a/src/simsopt/field/biotsavart_mod.py b/src/simsopt/field/biotsavart_mod.py deleted file mode 100644 index 08ce7bcb4..000000000 --- a/src/simsopt/field/biotsavart_mod.py +++ /dev/null @@ -1 +0,0 @@ -# self field for force optimization \ No newline at end of file From 1d790b969e6eaff2de997f8e50486b3a3279f4f0 Mon Sep 17 00:00:00 2001 From: phuslage <48801106+phuslage@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:05:24 +0200 Subject: [PATCH 23/61] Delete self_field.py --- src/simsopt/field/self_field.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/simsopt/field/self_field.py diff --git a/src/simsopt/field/self_field.py b/src/simsopt/field/self_field.py deleted file mode 100644 index ba7b17acf..000000000 --- a/src/simsopt/field/self_field.py +++ /dev/null @@ -1 +0,0 @@ -# self field calculation From 645add2f6ceb10a885dca89317d4cab36d79608b Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Fri, 1 Sep 2023 14:52:51 +0200 Subject: [PATCH 24/61] minor changes --- examples/2_Intermediate/strain_optimization_simple.py | 5 +++-- examples/3_Advanced/strain_simple.py | 10 ++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/2_Intermediate/strain_optimization_simple.py b/examples/2_Intermediate/strain_optimization_simple.py index 5f22b6009..14118dae3 100644 --- a/examples/2_Intermediate/strain_optimization_simple.py +++ b/examples/2_Intermediate/strain_optimization_simple.py @@ -1,7 +1,7 @@ """ This script performs an optimization of the HTS tape winding angle with respect to binormal curvature and torsional strain cost functions. -The orientation of the tape is defined wrt the Frener-Serret Frame +The orientation of the tape is defined wrt the Frenet-Serret Frame """ import numpy as np @@ -34,7 +34,8 @@ cur_threshold = 0.02 Jtor = LPTorsionalStrainPenalty(framedcurve, p=2, threshold=tor_threshold) -Jbin = LPBinormalCurvatureStrainPenalty(framedcurve, p=2, threshold=cur_threshold) +Jbin = LPBinormalCurvatureStrainPenalty( + framedcurve, p=2, threshold=cur_threshold) strain = StrainOpt(framedcurve, width) JF = Jtor + Jbin diff --git a/examples/3_Advanced/strain_simple.py b/examples/3_Advanced/strain_simple.py index 23214d37d..371772fac 100644 --- a/examples/3_Advanced/strain_simple.py +++ b/examples/3_Advanced/strain_simple.py @@ -1,7 +1,6 @@ """ -This script performs a stage two coil optimization -with additional terms for torsion and binormal curvature in the cost function. -We use a multifilament approach that initializes the coils using the Frener-Serret Frame +This script shows terms for torsion and binormal curvature to be used in a coil optimization cost function. +We use a multifilament approach that initializes the coils using the Frenet-Serret Frame. """ import os @@ -16,9 +15,8 @@ from simsopt.geo import ArclengthVariation from simsopt.objectives import SquaredFlux from simsopt.objectives import QuadraticPenalty -from simsopt.geo.strain_optimization_classes import StrainOpt +from simsopt.geo.strain_optimization import StrainOpt from simsopt.geo import create_multifilament_grid, ZeroRotation, FramedCurveFrenet, FrameRotation -# from exportcoils import export_coils, import_coils, import_coils_fb, export_coils_fb from simsopt.configs import get_ncsx_data import matplotlib.pyplot as plt @@ -28,7 +26,7 @@ # Set up the winding pack rot_order = 5 # order of the Fourier expression for the rotation of the filament pack -width = 12 +width = 0.012 rotation = FrameRotation(curve.quadpoints, rot_order) framedcurve = FramedCurveFrenet(curve, rotation) From b1c9c5948bf529d2747a3b0da7f65be59393ddd2 Mon Sep 17 00:00:00 2001 From: Paul Huslage Date: Fri, 1 Sep 2023 14:53:55 +0200 Subject: [PATCH 25/61] more minor changes --- src/simsopt/geo/strain_optimization.py | 45 ++++++++++++++++---------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/simsopt/geo/strain_optimization.py b/src/simsopt/geo/strain_optimization.py index e3499bc1c..7957e52fc 100644 --- a/src/simsopt/geo/strain_optimization.py +++ b/src/simsopt/geo/strain_optimization.py @@ -8,7 +8,8 @@ from simsopt._core.derivative import derivative_dec from simsopt.geo.curveobjectives import Lp_torsion_pure -__all__ = ['LPBinormalCurvatureStrainPenalty', 'LPTorsionalStrainPenalty', 'StrainOpt'] +__all__ = ['LPBinormalCurvatureStrainPenalty', + 'LPTorsionalStrainPenalty', 'StrainOpt'] class LPBinormalCurvatureStrainPenalty(Optimizable): @@ -26,14 +27,17 @@ def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.framedcurve = framedcurve self.strain = StrainOpt(framedcurve, width) self.width = width - self.p = p - self.threshold = threshold - self.J_jax = jit(lambda binorm, gammadash: Lp_torsion_pure(binorm, gammadash, p, threshold)) - self.grad0 = jit(lambda binorm, gammadash: grad(self.J_jax, argnums=0)(binorm, gammadash)) - self.grad1 = jit(lambda binorm, gammadash: grad(self.J_jax, argnums=1)(binorm, gammadash)) + self.p = p + self.threshold = threshold + self.J_jax = jit(lambda binorm, gammadash: Lp_torsion_pure( + binorm, gammadash, p, threshold)) + self.grad0 = jit(lambda binorm, gammadash: grad( + self.J_jax, argnums=0)(binorm, gammadash)) + self.grad1 = jit(lambda binorm, gammadash: grad( + self.J_jax, argnums=1)(binorm, gammadash)) super().__init__(depends_on=[framedcurve]) - def J(self): + def J(self): """ This returns the value of the quantity. """ @@ -44,9 +48,12 @@ def dJ(self): """ This returns the derivative of the quantity with respect to the curve and rotation dofs. """ - grad0 = self.grad0(self.strain.binormal_curvature_strain(), self.framedcurve.curve.gammadash()) - grad1 = self.grad1(self.strain.binormal_curvature_strain(), self.framedcurve.curve.gammadash()) - vjp0 = self.strain.binormstrain_vjp(self.framedcurve.frame_binormal_curvature(), self.width, grad0) + grad0 = self.grad0(self.strain.binormal_curvature_strain(), + self.framedcurve.curve.gammadash()) + grad1 = self.grad1(self.strain.binormal_curvature_strain(), + self.framedcurve.curve.gammadash()) + vjp0 = self.strain.binormstrain_vjp( + self.framedcurve.frame_binormal_curvature(), self.width, grad0) return self.framedcurve.dframe_binormal_curvature_by_dcoeff_vjp(vjp0) \ + self.framedcurve.curve.dgammadash_by_dcoeff_vjp(grad1) @@ -68,9 +75,10 @@ def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.framedcurve = framedcurve self.strain = StrainOpt(framedcurve, width) self.width = width - self.p = p - self.threshold = threshold - self.J_jax = jit(lambda torsion, gammadash: Lp_torsion_pure(torsion, gammadash, p, threshold)) + self.p = p + self.threshold = threshold + self.J_jax = jit(lambda torsion, gammadash: Lp_torsion_pure( + torsion, gammadash, p, threshold)) self.grad0 = jit(lambda torsion, gammadash: grad( self.J_jax, argnums=0)(torsion, gammadash)) self.grad1 = jit(lambda torsion, gammadash: grad( @@ -88,9 +96,12 @@ def dJ(self): """ This returns the derivative of the quantity with respect to the curve and rotation dofs. """ - grad0 = self.grad0(self.strain.torsional_strain(), self.framedcurve.curve.gammadash()) - grad1 = self.grad1(self.strain.torsional_strain(), self.framedcurve.curve.gammadash()) - vjp0 = self.strain.torstrain_vjp(self.framedcurve.frame_torsion(), self.width, grad0) + grad0 = self.grad0(self.strain.torsional_strain(), + self.framedcurve.curve.gammadash()) + grad1 = self.grad1(self.strain.torsional_strain(), + self.framedcurve.curve.gammadash()) + vjp0 = self.strain.torstrain_vjp( + self.framedcurve.frame_torsion(), self.width, grad0) return self.framedcurve.dframe_torsion_by_dcoeff_vjp(vjp0) \ + self.framedcurve.curve.dgammadash_by_dcoeff_vjp(grad1) @@ -100,7 +111,7 @@ def dJ(self): class StrainOpt(Optimizable): r""" This class evaluates the torsional and binormal curvature strains on HTS, based on - a filamentary model of the coil and the orientation of the HTX tape. + a filamentary model of the coil and the orientation of the HTS tape. As defined in, From f698fc84e4aa9bc9dc1bb7473fa63b1144f91c68 Mon Sep 17 00:00:00 2001 From: Matt Landreman Date: Sat, 2 Sep 2023 15:07:21 -0400 Subject: [PATCH 26/61] Fix some docs formatting. Renamed strain examples --- docs/source/simsopt.geo.rst | 16 ++++++++++++++++ ...mization_simple.py => strain_optimization.py} | 9 +++------ .../{strain_simple.py => strain_plot.py} | 2 ++ src/simsopt/geo/framedcurve.py | 8 +++++--- 4 files changed, 26 insertions(+), 9 deletions(-) rename examples/2_Intermediate/{strain_optimization_simple.py => strain_optimization.py} (85%) mode change 100644 => 100755 rename examples/3_Advanced/{strain_simple.py => strain_plot.py} (98%) mode change 100644 => 100755 diff --git a/docs/source/simsopt.geo.rst b/docs/source/simsopt.geo.rst index 1d706c60a..6302da7f6 100644 --- a/docs/source/simsopt.geo.rst +++ b/docs/source/simsopt.geo.rst @@ -76,6 +76,14 @@ simsopt.geo.finitebuild module :undoc-members: :show-inheritance: +simsopt.geo.framedcurve module +------------------------------ + +.. automodule:: simsopt.geo.framedcurve + :members: + :undoc-members: + :show-inheritance: + simsopt.geo.jit module ---------------------- @@ -100,6 +108,14 @@ simsopt.geo.qfmsurface module :undoc-members: :show-inheritance: +simsopt.geo.strain_optimization module +-------------------------------------- + +.. automodule:: simsopt.geo.strain_optimization + :members: + :undoc-members: + :show-inheritance: + simsopt.geo.surface module -------------------------- diff --git a/examples/2_Intermediate/strain_optimization_simple.py b/examples/2_Intermediate/strain_optimization.py old mode 100644 new mode 100755 similarity index 85% rename from examples/2_Intermediate/strain_optimization_simple.py rename to examples/2_Intermediate/strain_optimization.py index 14118dae3..c4e5c6898 --- a/examples/2_Intermediate/strain_optimization_simple.py +++ b/examples/2_Intermediate/strain_optimization.py @@ -1,7 +1,9 @@ +#!/usr/bin/env python + """ This script performs an optimization of the HTS tape winding angle with respect to binormal curvature and torsional strain cost functions. -The orientation of the tape is defined wrt the Frenet-Serret Frame +The orientation of the tape is defined with respect to the Frenet-Serret Frame """ import numpy as np @@ -53,10 +55,5 @@ def fun(dofs): f = fun dofs = JF.x -print(""" -################################################################################ -### Run the optimisation ####################################################### -################################################################################ -""") res = minimize(fun, dofs, jac=True, method='L-BFGS-B', options={'maxiter': MAXITER, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) diff --git a/examples/3_Advanced/strain_simple.py b/examples/3_Advanced/strain_plot.py old mode 100644 new mode 100755 similarity index 98% rename from examples/3_Advanced/strain_simple.py rename to examples/3_Advanced/strain_plot.py index 371772fac..54b913691 --- a/examples/3_Advanced/strain_simple.py +++ b/examples/3_Advanced/strain_plot.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python + """ This script shows terms for torsion and binormal curvature to be used in a coil optimization cost function. We use a multifilament approach that initializes the coils using the Frenet-Serret Frame. diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index fdf8bda69..7cf627e16 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -34,12 +34,14 @@ def __init__(self, curve, rotation=None): class FramedCurveFrenet(FramedCurve): - """ + r""" Given a curve, one defines a reference frame using the Frenet normal and binormal vectors: tangent = dr/dl - normal = (dtangent/dl)/|dtangent/dl| + + normal = (dtangent/dl)/||dtangent/dl|| + binormal = tangent x normal In addition, we specify an angle along the curve that @@ -228,7 +230,7 @@ class FramedCurveCentroid(FramedCurve): def __init__(self, curve, rotation=None): FramedCurve.__init__(self, curve, rotation) - + self.torsion = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash: torsion_pure_centroid( gamma, gammadash, gammadashdash, alpha, alphadash)) self.torsiongrad_vjp0 = jit(lambda gamma, gammadash, gammadashdash, alpha, alphadash, v: vjp( From 8d3e607c8a70e0fea8be74212d129a237f8b5a79 Mon Sep 17 00:00:00 2001 From: Matt Landreman Date: Sat, 2 Sep 2023 15:13:08 -0400 Subject: [PATCH 27/61] Add strain_optimization example to CI --- examples/run_serial_examples | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/run_serial_examples b/examples/run_serial_examples index 91d49472d..8015560c8 100755 --- a/examples/run_serial_examples +++ b/examples/run_serial_examples @@ -17,4 +17,5 @@ set -ex ./2_Intermediate/stage_two_optimization.py ./2_Intermediate/stage_two_optimization_stochastic.py ./2_Intermediate/stage_two_optimization_finite_beta.py +./2_Intermediate/strain_optimization.py ./3_Advanced/stage_two_optimization_finitebuild.py From d04ec4c0023227af727d89eb956180b6c55244d3 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 11:54:59 -0400 Subject: [PATCH 28/61] Revert changes to CMakeLists.txt. --- CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a488b5423..72823d431 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ else() unset(COMPILER_SUPPORTS_MARCH_NATIVE CACHE) CHECK_CXX_COMPILER_FLAG(-march=native COMPILER_SUPPORTS_MARCH_NATIVE) if(COMPILER_SUPPORTS_MARCH_NATIVE) - set(CMAKE_CXX_FLAGS "-O3 -mfma -ffp-contract=fast") + set(CMAKE_CXX_FLAGS "-O3 -march=native -mfma -ffp-contract=fast") elseif(${CMAKE_HOST_SYSTEM_PROCESSOR} STREQUAL "arm64") set(CMAKE_CXX_FLAGS "-O3 -mcpu=apple-a14 -mfma -ffp-contract=fast") else() @@ -118,6 +118,8 @@ pybind11_add_module(${PROJECT_NAME} src/simsoptpp/regular_grid_interpolant_3d_py.cpp src/simsoptpp/curve.cpp src/simsoptpp/curverzfourier.cpp src/simsoptpp/curvexyzfourier.cpp src/simsoptpp/surface.cpp src/simsoptpp/surfacerzfourier.cpp src/simsoptpp/surfacexyzfourier.cpp + src/simsoptpp/integral_BdotN.cpp + src/simsoptpp/dipole_field.cpp src/simsoptpp/permanent_magnet_optimization.cpp src/simsoptpp/dommaschk.cpp src/simsoptpp/reiman.cpp src/simsoptpp/tracing.cpp src/simsoptpp/magneticfield_biotsavart.cpp src/simsoptpp/python_boozermagneticfield.cpp src/simsoptpp/boozerradialinterpolant.cpp @@ -134,7 +136,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE fmt::fmt-header-only) if(NOT Boost_FOUND) add_dependencies(${PROJECT_NAME} ${boost_target}) endif() -target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) +set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "-I${Boost_INCLUDE_DIRS}") # target_link_libraries(${PROJECT_NAME} PRIVATE pybind11::module Boost::headers) if(OpenMP_CXX_FOUND) From 6f7e6c65f30563cb40658acc12d7583274e25b96 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 12:23:23 -0400 Subject: [PATCH 29/61] Added Paz-Soldan reference to example. --- examples/2_Intermediate/strain_optimization.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/2_Intermediate/strain_optimization.py b/examples/2_Intermediate/strain_optimization.py index c4e5c6898..ff59a7ad7 100755 --- a/examples/2_Intermediate/strain_optimization.py +++ b/examples/2_Intermediate/strain_optimization.py @@ -2,7 +2,12 @@ """ This script performs an optimization of the HTS tape winding angle -with respect to binormal curvature and torsional strain cost functions. +with respect to binormal curvature and torsional strain cost functions as defined in + + Paz Soldan, "Non-planar coil winding angle optimization for compatibility with + non-insulated high-temperature superconducting magnets", Journal of Plasma Physics + 86 (2020), doi:10.1017/S0022377820001208. + The orientation of the tape is defined with respect to the Frenet-Serret Frame """ From d3cffb3c2a6a7eed3f129e8addc8f5a87e523e6a Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 12:25:42 -0400 Subject: [PATCH 30/61] Now using in_github_actions. --- examples/2_Intermediate/stage_two_optimization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/2_Intermediate/stage_two_optimization.py b/examples/2_Intermediate/stage_two_optimization.py index f470e3908..277fd0fcb 100755 --- a/examples/2_Intermediate/stage_two_optimization.py +++ b/examples/2_Intermediate/stage_two_optimization.py @@ -34,6 +34,7 @@ from simsopt.field import Current, coils_via_symmetries from simsopt.geo import CurveLength, CurveCurveDistance, \ MeanSquaredCurvature, LpCurveCurvature, CurveSurfaceDistance +from simsopt.util import in_github_actions # Number of unique coil shapes, i.e. the number of coils per half field period: # (Since the configuration has nfp = 2, multiply by 4 to get the total number of coils.) @@ -70,8 +71,7 @@ MSC_WEIGHT = 1e-6 # Number of iterations to perform: -ci = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] -MAXITER = 50 if ci else 400 +MAXITER = 50 if in_github_actions else 400 # File for the desired boundary magnetic surface: TEST_DIR = (Path(__file__).parent / ".." / ".." / "tests" / "test_files").resolve() From 3728c0b00de7449c3470b130c2915add11352aa3 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 12:26:45 -0400 Subject: [PATCH 31/61] Removed strain_plot.py. --- examples/3_Advanced/strain_plot.py | 52 ------------------------------ 1 file changed, 52 deletions(-) delete mode 100755 examples/3_Advanced/strain_plot.py diff --git a/examples/3_Advanced/strain_plot.py b/examples/3_Advanced/strain_plot.py deleted file mode 100755 index 54b913691..000000000 --- a/examples/3_Advanced/strain_plot.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python - -""" -This script shows terms for torsion and binormal curvature to be used in a coil optimization cost function. -We use a multifilament approach that initializes the coils using the Frenet-Serret Frame. -""" - -import os -from pathlib import Path -import numpy as np -from scipy.optimize import minimize -from simsopt.field import BiotSavart -from simsopt.field import Current, Coil, apply_symmetries_to_curves, apply_symmetries_to_currents -from simsopt.geo import curves_to_vtk, create_equally_spaced_curves -from simsopt.geo import CurveLength, CurveCurveDistance, CurveSurfaceDistance -from simsopt.geo import SurfaceRZFourier -from simsopt.geo import ArclengthVariation -from simsopt.objectives import SquaredFlux -from simsopt.objectives import QuadraticPenalty -from simsopt.geo.strain_optimization import StrainOpt -from simsopt.geo import create_multifilament_grid, ZeroRotation, FramedCurveFrenet, FrameRotation -from simsopt.configs import get_ncsx_data -import matplotlib.pyplot as plt - -curves, currents, ma = get_ncsx_data() -curve = curves[0] - -# Set up the winding pack -rot_order = 5 # order of the Fourier expression for the rotation of the filament pack - -width = 0.012 - -rotation = FrameRotation(curve.quadpoints, rot_order) -framedcurve = FramedCurveFrenet(curve, rotation) - -strain = StrainOpt(framedcurve, width=width) - -tor = strain.binormal_curvature_strain() - -plt.figure() -plt.plot(tor) -plt.show() - -# tor = strain.torsional_strain() -tor_frame = framedcurve.frame_torsion() -tor_curve = curve.torsion() -dl = curve.incremental_arclength() - -plt.figure() -plt.plot(tor_frame) -plt.plot(tor_curve) -plt.show() From a3152d543ab27df67ee65b0d63bc2451b80e39c1 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 12:28:00 -0400 Subject: [PATCH 32/61] Removed unneeded imports. --- src/simsopt/geo/strain_optimization.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/simsopt/geo/strain_optimization.py b/src/simsopt/geo/strain_optimization.py index 7957e52fc..9246eee60 100644 --- a/src/simsopt/geo/strain_optimization.py +++ b/src/simsopt/geo/strain_optimization.py @@ -3,7 +3,6 @@ from jax import vjp, jvp, grad import simsoptpp as sopp from simsopt.geo.jit import jit -from simsopt.geo import ZeroRotation, Curve from simsopt._core import Optimizable from simsopt._core.derivative import derivative_dec from simsopt.geo.curveobjectives import Lp_torsion_pure From e565c9251391a6b4f69f8dacfcd19824afe54233 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 12:28:25 -0400 Subject: [PATCH 33/61] Removed unneeded imports. --- src/simsopt/geo/strain_optimization.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/simsopt/geo/strain_optimization.py b/src/simsopt/geo/strain_optimization.py index 9246eee60..cf37ad969 100644 --- a/src/simsopt/geo/strain_optimization.py +++ b/src/simsopt/geo/strain_optimization.py @@ -1,4 +1,3 @@ -import numpy as np import jax.numpy as jnp from jax import vjp, jvp, grad import simsoptpp as sopp From 764176513c8afd7fe4995eb13fa6645dc37c0ce6 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 13:23:03 -0400 Subject: [PATCH 34/61] Remove unused imports. --- examples/2_Intermediate/strain_optimization.py | 8 ++------ src/simsopt/geo/strain_optimization.py | 3 +-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/examples/2_Intermediate/strain_optimization.py b/examples/2_Intermediate/strain_optimization.py index ff59a7ad7..5087d4fd4 100755 --- a/examples/2_Intermediate/strain_optimization.py +++ b/examples/2_Intermediate/strain_optimization.py @@ -11,18 +11,14 @@ The orientation of the tape is defined with respect to the Frenet-Serret Frame """ -import numpy as np -import os import numpy as np from scipy.optimize import minimize from simsopt.geo import StrainOpt, LPTorsionalStrainPenalty, LPBinormalCurvatureStrainPenalty from simsopt.geo import FrameRotation, FramedCurveFrenet, CurveXYZFourier -from simsopt.field import load_coils_from_makegrid_file from simsopt.configs import get_hsx_data -import matplotlib.pyplot as plt +from simsopt.util import in_github_actions -ci = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] -MAXITER = 50 if ci else 400 +MAXITER = 50 if in_github_actions else 400 curves, currents, ma = get_hsx_data(Nt_coils=10, ppp=10) curve = curves[1] diff --git a/src/simsopt/geo/strain_optimization.py b/src/simsopt/geo/strain_optimization.py index cf37ad969..cbe7ac5d9 100644 --- a/src/simsopt/geo/strain_optimization.py +++ b/src/simsopt/geo/strain_optimization.py @@ -1,6 +1,5 @@ import jax.numpy as jnp -from jax import vjp, jvp, grad -import simsoptpp as sopp +from jax import vjp, grad from simsopt.geo.jit import jit from simsopt._core import Optimizable from simsopt._core.derivative import derivative_dec From 77c178798941ad0cc80ab462c1bb92195d4d8cca Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 13:24:32 -0400 Subject: [PATCH 35/61] Remove unused commits. --- src/simsopt/geo/framedcurve.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index 7cf627e16..03fa7c7d6 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -1,7 +1,6 @@ import numpy as np import jax.numpy as jnp -from jax import vjp, jvp, grad - +from jax import vjp, jvp import simsoptpp as sopp from .._core.optimizable import Optimizable from .._core.derivative import Derivative From 50eee3f6603e241e6c0e6a2cfdbb94069464c93e Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 13:29:11 -0400 Subject: [PATCH 36/61] Responding to comments. --- src/simsopt/geo/__init__.py | 3 ++- src/simsopt/geo/framedcurve.py | 25 ------------------------- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/simsopt/geo/__init__.py b/src/simsopt/geo/__init__.py index a3d1dd1d6..7450be3d5 100644 --- a/src/simsopt/geo/__init__.py +++ b/src/simsopt/geo/__init__.py @@ -31,4 +31,5 @@ surface.__all__ + surfacegarabedian.__all__ + surfacehenneberg.__all__ + surfacerzfourier.__all__ + surfacexyzfourier.__all__ + - surfacexyztensorfourier.__all__ + surfaceobjectives.__all__) + surfacexyztensorfourier.__all__ + surfaceobjectives.__all__ + strain_optimization.__all__ + framed_curve.__all__) diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index 03fa7c7d6..d580d5e4b 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -285,31 +285,6 @@ def rotated_frame_dash(self): self.rotation.alpha(self.curve.quadpoints), self.rotation.alphadash(self.curve.quadpoints) ) - def d_frame_torsion(self): - gamma = self.curve.gamma() - d1gamma = self.curve.gammadash() - d2gamma = self.curve.gammadashdash() - alpha = self.rotation.alpha(self.curve.quadpoints) - alphadash = self.rotation.alphadash(self.curve.quadpoints) - - grad0 = self.torsiongrad0(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) - grad1 = self.torsiongrad1(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) - grad2 = self.torsiongrad2(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) - grad4 = self.torsiongrad4(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) - grad5 = self.torsiongrad5(gamma, d1gamma, d2gamma, - d3gamma, alpha, alphadash) - - return self.curve.dgamma_by_dcoeff_vjp(grad0) \ - + self.curve.dgammadash_by_dcoeff_vjp(grad1) \ - + self.curve.dgammadashdash_by_dcoeff_vjp(grad2) \ - + self.curve.dgammadashdashdash_by_dcoeff_vjp(grad3) \ - + self.rotation.dalpha_by_dcoeff_vjp(self.curve.quadpoints, grad4) \ - + self.rotation.dalphadash_by_dcoeff_vjp(self.curve.quadpoints, grad5) - def rotated_frame_dcoeff_vjp(self, v, dn, db, arg=0): assert arg in [0, 1, 2, 3] g = self.curve.gamma() From 468ed7409dff16d341c5ae51b26e5a7389b4d98e Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 13:34:18 -0400 Subject: [PATCH 37/61] Autopep. --- src/simsopt/geo/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/geo/__init__.py b/src/simsopt/geo/__init__.py index 7450be3d5..d9a0fd960 100644 --- a/src/simsopt/geo/__init__.py +++ b/src/simsopt/geo/__init__.py @@ -31,5 +31,5 @@ surface.__all__ + surfacegarabedian.__all__ + surfacehenneberg.__all__ + surfacerzfourier.__all__ + surfacexyzfourier.__all__ + - surfacexyztensorfourier.__all__ + surfaceobjectives.__all__ + surfacexyztensorfourier.__all__ + surfaceobjectives.__all__ + strain_optimization.__all__ + framed_curve.__all__) From c41f5afcf42a433a1c05a4b1d2fea7bec38356db Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 13:45:29 -0400 Subject: [PATCH 38/61] Fixing build errors. --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 72823d431..22e93dfaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,8 +118,6 @@ pybind11_add_module(${PROJECT_NAME} src/simsoptpp/regular_grid_interpolant_3d_py.cpp src/simsoptpp/curve.cpp src/simsoptpp/curverzfourier.cpp src/simsoptpp/curvexyzfourier.cpp src/simsoptpp/surface.cpp src/simsoptpp/surfacerzfourier.cpp src/simsoptpp/surfacexyzfourier.cpp - src/simsoptpp/integral_BdotN.cpp - src/simsoptpp/dipole_field.cpp src/simsoptpp/permanent_magnet_optimization.cpp src/simsoptpp/dommaschk.cpp src/simsoptpp/reiman.cpp src/simsoptpp/tracing.cpp src/simsoptpp/magneticfield_biotsavart.cpp src/simsoptpp/python_boozermagneticfield.cpp src/simsoptpp/boozerradialinterpolant.cpp From 054ecd89896cc2279c69ffaad55d6dfe76279c7a Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 13:59:06 -0400 Subject: [PATCH 39/61] Adding required import. --- src/simsopt/geo/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/simsopt/geo/__init__.py b/src/simsopt/geo/__init__.py index d9a0fd960..b6ef5799b 100644 --- a/src/simsopt/geo/__init__.py +++ b/src/simsopt/geo/__init__.py @@ -22,6 +22,7 @@ from .surfacexyzfourier import * from .surfacexyztensorfourier import * from .strain_optimization import * +from .framed_curve import * __all__ = (curve.__all__ + curvehelical.__all__ + curverzfourier.__all__ + curvexyzfourier.__all__ + From 9a1c51f1428afa1d85487bc38afe2dc354aa5bba Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 14:08:40 -0400 Subject: [PATCH 40/61] Fix typo. --- src/simsopt/geo/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/simsopt/geo/__init__.py b/src/simsopt/geo/__init__.py index b6ef5799b..625f9c2ca 100644 --- a/src/simsopt/geo/__init__.py +++ b/src/simsopt/geo/__init__.py @@ -22,7 +22,7 @@ from .surfacexyzfourier import * from .surfacexyztensorfourier import * from .strain_optimization import * -from .framed_curve import * +from .framedcurve import * __all__ = (curve.__all__ + curvehelical.__all__ + curverzfourier.__all__ + curvexyzfourier.__all__ + @@ -33,4 +33,4 @@ surfacegarabedian.__all__ + surfacehenneberg.__all__ + surfacerzfourier.__all__ + surfacexyzfourier.__all__ + surfacexyztensorfourier.__all__ + surfaceobjectives.__all__ + - strain_optimization.__all__ + framed_curve.__all__) + strain_optimization.__all__ + framedcurve.__all__) From 077462e015de7da359a78af44da3c23a128754c6 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 14:32:08 -0400 Subject: [PATCH 41/61] Including in_github_actions. --- src/simsopt/util/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/simsopt/util/__init__.py b/src/simsopt/util/__init__.py index 3367d9c4a..b7dd8dace 100644 --- a/src/simsopt/util/__init__.py +++ b/src/simsopt/util/__init__.py @@ -1,4 +1,13 @@ +import os + from .mpi import * from .logger import * -__all__ = (mpi.__all__ + logger.__all__) +"""Boolean indicating if we are in the GitHub actions CI""" +in_github_actions = "CI" in os.environ and os.environ['CI'].lower() in ['1', 'true'] + +__all__ = ( + mpi.__all__ + + logger.__all__ + + ['in_github_actions'] +) From 705053ac17a926a8b9d55c010e3659bfc1082d53 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Sat, 9 Sep 2023 15:23:00 -0400 Subject: [PATCH 42/61] Tweak to test to change coverage. --- tests/geo/test_strainopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/geo/test_strainopt.py b/tests/geo/test_strainopt.py index 4fb7ef147..917c2d42c 100644 --- a/tests/geo/test_strainopt.py +++ b/tests/geo/test_strainopt.py @@ -31,7 +31,7 @@ def subtest_binormal_curvature(self, order, centroid): assert np.allclose(rotation.x, rotationShared.x) assert np.allclose(rotation.alpha(c.quadpoints), rotationShared.alpha(c.quadpoints)) else: - rotation = ZeroRotation(c.quadpoints) + rotation = None if centroid: framedcurve = FramedCurveCentroid(c, rotation) From 3bb191badbe3f96fb710ed7ced5eb4d862117f9c Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 13 Sep 2023 12:51:56 +0200 Subject: [PATCH 43/61] change freeboundary test to read nonstellsymmetric equilibrium --- tests/mhd/test_spec.py | 21 +- tests/test_files/M16N08.sp | 1216 ++++++++++++++++++++++++++++++++++++ 2 files changed, 1236 insertions(+), 1 deletion(-) create mode 100644 tests/test_files/M16N08.sp diff --git a/tests/mhd/test_spec.py b/tests/mhd/test_spec.py index e5ce31d47..96c795722 100755 --- a/tests/mhd/test_spec.py +++ b/tests/mhd/test_spec.py @@ -78,6 +78,25 @@ def test_init_from_file(self): self.assertAlmostEqual(s.boundary.get_rc(0, 1), 0.1, places=places) self.assertAlmostEqual(s.boundary.get_zs(0, 1), 0.1, places=places) + def test_init_freeboundary_nonstellsym(self): + """ + Try creating a Spec instance from a freeboundary file that is also + non-stellarator symmetric. + Check value of normal field + """ + + filename = os.path.join(TEST_DIR, 'M16n08.sp') + + with ScratchDir("."): + s = Spec(filename) + + places = 7 + self.assertAlmostEqual(s.normal_field.get_vns(0, 1), 3.615260745287559e-04, places) + self.assertAlmostEqual(s.normal_field.get_vns(3, -1), 1.714666510000000E-03, places) + self.assertAlmostEqual(s.normal_field.get_vnc(1, 0), 1.924871538367248e-04, places) + self.assertAlmostEqual(s.normal_field.get_vnc(1, -2), 4.070523669489626e-04, places) + + def test_init_freeboundary(self): """ Try creating a Spec instance from a freeboundary file. Check value @@ -91,7 +110,7 @@ def test_init_freeboundary(self): places = 5 self.assertAlmostEqual(s.normal_field.get_vns(0, 1), -1.116910580000000E-04, places) - self.assertAlmostEqual(s.normal_field.get_vns(3, -1), 1.714666510000000E-03, places) + self.assertAlmostEqual(s.normal_field.get_vns(3, -1), -1.269776831212886e-04, places) def test_run(self): """ diff --git a/tests/test_files/M16N08.sp b/tests/test_files/M16N08.sp new file mode 100644 index 000000000..7d468a649 --- /dev/null +++ b/tests/test_files/M16N08.sp @@ -0,0 +1,1216 @@ +&physicslist + igeometry = 3 , + istellsym = 0 , + lfreebound = 1 , + phiedge = 1.005620063627583e+00 , + curtor = -1.530482407474665e-10 , + curpol = 6.972178908886571e+01 , + gamma = 0.000000000000000e+00 , + nvol = 2 , + nfp = 2 , + mpol = 16 , + ntor = 8 , + lconstraint = 0 , + pscale = 0.000000000000000e+00 , + ladiabatic = 0 , + mupftol = 1.000000000000000e-16 , + mupfits = 16 , + tflux = 2.500000000000000e-01 1.000000000000000e+00 2.299984613552971e+00 + pflux = 0.000000000000000e+00 -6.108897011942874e-02 -1.435763017312650e-01 + helicity = -2.576694051484501e-03 6.087102545397132e-06 -6.213605462768436e-05 + mu = 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 + pressure = 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 + adiabatic = 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 + pl = 0 0 0 0 + ql = 0 1 0 0 + pr = 0 1 0 0 + qr = 0 13 0 0 + iota = 0.000000000000000e+00 7.343203878225875e-02 0.000000000000000e+00 0.000000000000000e+00 + lp = 0 0 0 0 + lq = 0 1 0 0 + rp = 0 1 0 0 + rq = 0 13 0 0 + oita = 0.000000000000000e+00 7.343203878225875e-02 0.000000000000000e+00 0.000000000000000e+00 + Lrad = 8 8 8 + Rac = 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 + Zas = 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 + Ras = 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 + Zac = 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 0.000000000000000e+00 + Rbc(0,0) = 3.000891568182305e+00 Zbs(0,0) = 0.000000000000000e+00 Rbs(0,0) = 0.000000000000000e+00 Zbc(0,0) = 1.977985854662758e-04 + Rbc(1,0) = 1.549584617912061e-04 Zbs(1,0) = -5.980912307968718e-02 Rbs(1,0) = 2.988418381825432e-02 Zbc(1,0) = 8.986339895053900e-05 + Rbc(2,0) = 1.079033239987434e-05 Zbs(2,0) = 9.295363299317108e-06 Rbs(2,0) = 1.131277816928571e-05 Zbc(2,0) = -6.310284361237698e-06 + Rbc(3,0) = 4.824230315394798e-07 Zbs(3,0) = -1.153962330568762e-07 Rbs(3,0) = -1.560321108793688e-06 Zbc(3,0) = 1.715870237613191e-06 + Rbc(4,0) = -1.396359657407556e-07 Zbs(4,0) = -1.153155943646326e-07 Rbs(4,0) = -1.806451705037117e-08 Zbc(4,0) = -4.040969821267141e-08 + Rbc(5,0) = 2.017099127964016e-08 Zbs(5,0) = 1.401507246269720e-08 Rbs(5,0) = 2.414830238655452e-08 Zbc(5,0) = -9.527831267414959e-09 + Rbc(6,0) = 6.925434265595456e-09 Zbs(6,0) = -1.303343366244095e-11 Rbs(6,0) = 8.069397553975789e-09 Zbc(6,0) = -1.037418766943065e-09 + Rbc(7,0) = 3.675422456724106e-09 Zbs(7,0) = -1.113269211802120e-09 Rbs(7,0) = 5.259989253996036e-09 Zbc(7,0) = -6.387169047049453e-10 + Rbc(8,0) = 2.448119131401051e-09 Zbs(8,0) = -7.268914318436959e-10 Rbs(8,0) = 3.665286766753462e-09 Zbc(8,0) = -1.623395000235289e-10 + Rbc(-8,1) = 2.728126549827848e-09 Zbs(-8,1) = -1.918785786242337e-09 Rbs(-8,1) = -3.314765448552290e-09 Zbc(-8,1) = -3.478356622638992e-09 + Rbc(-7,1) = 3.940008580407160e-09 Zbs(-7,1) = -3.278866514409701e-09 Rbs(-7,1) = -3.796826165502953e-09 Zbc(-7,1) = -5.285499205832392e-09 + Rbc(-6,1) = 5.486921553165711e-09 Zbs(-6,1) = -4.688779096258160e-09 Rbs(-6,1) = -5.034738592542716e-09 Zbc(-6,1) = -7.153942044142097e-09 + Rbc(-5,1) = 7.865427799766353e-09 Zbs(-5,1) = -5.127008266028507e-09 Rbs(-5,1) = -1.464611671305431e-08 Zbc(-5,1) = -1.569337198542345e-08 + Rbc(-4,1) = -1.674938050731462e-07 Zbs(-4,1) = 1.342466047628991e-07 Rbs(-4,1) = 8.428990513324408e-11 Zbc(-4,1) = -3.362675003302608e-08 + Rbc(-3,1) = 1.216780438593311e-07 Zbs(-3,1) = -1.349823049404980e-07 Rbs(-3,1) = 4.011485145733234e-06 Zbc(-3,1) = 3.893088440686576e-06 + Rbc(-2,1) = 1.043858713065171e-04 Zbs(-2,1) = -1.054589289336694e-04 Rbs(-2,1) = -5.487316991492441e-07 Zbc(-2,1) = -7.603827742293734e-07 + Rbc(-1,1) = -1.784340720809674e-05 Zbs(-1,1) = -4.035719978185197e-06 Rbs(-1,1) = -2.691252596101455e-03 Zbc(-1,1) = -2.703990464548454e-03 + Rbc(0,1) = 2.985157256456206e-01 Zbs(0,1) = -2.982171224132523e-01 Rbs(0,1) = 4.251806119524189e-04 Zbc(0,1) = -4.273361484487426e-04 + Rbc(1,1) = -4.974830093880376e-02 Zbs(1,1) = -4.974775916588486e-02 Rbs(1,1) = 2.502603653136018e-02 Zbc(1,1) = 1.198105172091764e-04 + Rbc(2,1) = 3.483764396893520e-05 Zbs(2,1) = 9.183647372250326e-06 Rbs(2,1) = 6.383001540546234e-05 Zbc(2,1) = -6.394267892627056e-05 + Rbc(3,1) = 3.463948395611681e-06 Zbs(3,1) = 3.351712834490921e-06 Rbs(3,1) = -2.047246911240814e-06 Zbc(3,1) = 1.198313829388473e-06 + Rbc(4,1) = -1.791364720584111e-07 Zbs(4,1) = -1.437298811982069e-07 Rbs(4,1) = -5.257381648505548e-08 Zbc(4,1) = 7.905992894395630e-08 + Rbc(5,1) = -1.633079108054013e-08 Zbs(5,1) = -2.293209524292142e-08 Rbs(5,1) = 7.156481707891762e-09 Zbc(5,1) = 1.069828297878333e-08 + Rbc(6,1) = -4.753312368659780e-10 Zbs(6,1) = -1.015158222606543e-08 Rbs(6,1) = 3.681556575455224e-09 Zbc(6,1) = 9.173682576757014e-09 + Rbc(7,1) = 1.714231680739782e-09 Zbs(7,1) = -5.185281392897838e-09 Rbs(7,1) = 4.216544521672336e-09 Zbc(7,1) = 5.131893684835592e-09 + Rbc(8,1) = 1.404117604594337e-09 Zbs(8,1) = -3.114598000041755e-09 Rbs(8,1) = 3.303760271115836e-09 Zbc(8,1) = 3.640300306521178e-09 + Rbc(-8,2) = 1.101035780833025e-09 Zbs(-8,2) = -1.069653611243209e-09 Rbs(-8,2) = -1.147733206147572e-09 Zbc(-8,2) = -1.095578465603304e-09 + Rbc(-7,2) = 1.537157215856869e-09 Zbs(-7,2) = -1.839958597380168e-09 Rbs(-7,2) = -1.306200557173264e-09 Zbc(-7,2) = -1.832606669960485e-09 + Rbc(-6,2) = 2.294665706380220e-09 Zbs(-6,2) = -2.549632409153949e-09 Rbs(-6,2) = -2.008542894014176e-09 Zbc(-6,2) = -2.689714171842857e-09 + Rbc(-5,2) = 4.060316403880747e-09 Zbs(-5,2) = -4.815711633833853e-09 Rbs(-5,2) = -3.038099738221138e-09 Zbc(-5,2) = -5.177924353409689e-09 + Rbc(-4,2) = -1.766323145294325e-08 Zbs(-4,2) = 9.867460508483144e-10 Rbs(-4,2) = -3.381174375264974e-08 Zbc(-4,2) = -2.405795579416215e-08 + Rbc(-3,2) = -3.440769518012183e-07 Zbs(-3,2) = 3.441934429159791e-07 Rbs(-3,2) = 3.445996813270162e-07 Zbc(-3,2) = 4.155156265547598e-09 + Rbc(-2,2) = 3.686280480907342e-06 Zbs(-2,2) = -1.774921041728090e-06 Rbs(-2,2) = 4.673460051241489e-06 Zbc(-2,2) = 5.286802687038037e-06 + Rbc(-1,2) = 2.781973994516649e-05 Zbs(-1,2) = -1.341308211459195e-05 Rbs(-1,2) = -5.745039784221623e-05 Zbc(-1,2) = -5.001925553258801e-05 + Rbc(0,2) = -7.727063925344844e-04 Zbs(0,2) = 1.179134232382115e-03 Rbs(0,2) = -1.137624390967074e-04 Zbc(0,2) = 1.540407463836563e-04 + Rbc(1,2) = -2.049734998719606e-04 Zbs(1,2) = 6.993954609714700e-04 Rbs(1,2) = 9.662607175286411e-05 Zbc(1,2) = 1.247623746628514e-04 + Rbc(2,2) = 1.513313246301526e-04 Zbs(2,2) = 1.602479841280785e-04 Rbs(2,2) = -9.850493024029867e-05 Zbc(2,2) = 8.504351019461247e-05 + Rbc(3,2) = -4.863217023237372e-06 Zbs(3,2) = -5.355360252279489e-06 Rbs(3,2) = -1.076240377098182e-05 Zbc(3,2) = 1.144478347671509e-05 + Rbc(4,2) = -3.162342256318263e-07 Zbs(4,2) = -3.493488392576747e-07 Rbs(4,2) = 2.581452258913891e-07 Zbc(4,2) = -2.020859822738473e-07 + Rbc(5,2) = 9.028529511193416e-08 Zbs(5,2) = 7.389074710149153e-08 Rbs(5,2) = -2.577693557261565e-09 Zbc(5,2) = 1.377589935875461e-08 + Rbc(6,2) = 1.092758745633040e-08 Zbs(6,2) = 6.327216115870605e-09 Rbs(6,2) = -6.109331595113325e-09 Zbc(6,2) = 1.394390158100275e-08 + Rbc(7,2) = 8.046387363882840e-10 Zbs(7,2) = -1.547202948515446e-09 Rbs(7,2) = 1.570808509909518e-09 Zbc(7,2) = 3.148712808505356e-09 + Rbc(8,2) = 5.700577998897687e-10 Zbs(8,2) = -1.070682268710484e-09 Rbs(8,2) = 1.147423604108286e-09 Zbc(8,2) = 1.850231493476768e-09 + Rbc(-8,3) = 4.208721164874012e-10 Zbs(-8,3) = -4.296290571694143e-10 Rbs(-8,3) = 1.003300388660581e-10 Zbc(-8,3) = 9.899600060562722e-11 + Rbc(-7,3) = 4.365496432621360e-10 Zbs(-7,3) = -6.396557541365763e-10 Rbs(-7,3) = 2.498140306959090e-10 Zbc(-7,3) = 2.553354074071620e-10 + Rbc(-6,3) = 7.160671719138351e-10 Zbs(-6,3) = -9.403080654565421e-10 Rbs(-6,3) = 3.163765212554030e-10 Zbc(-6,3) = 4.130018878760263e-10 + Rbc(-5,3) = 9.844048763013072e-10 Zbs(-5,3) = -2.283403374652330e-09 Rbs(-5,3) = 3.974770055867862e-11 Zbc(-5,3) = -4.282372144150836e-10 + Rbc(-4,3) = -5.718415906626132e-09 Zbs(-4,3) = 5.976646224710108e-09 Rbs(-4,3) = 3.677376585553809e-10 Zbc(-4,3) = 7.218391477682015e-09 + Rbc(-3,3) = -6.950616084800681e-08 Zbs(-3,3) = -3.687559491984598e-08 Rbs(-3,3) = 7.510377611710683e-08 Zbc(-3,3) = -8.544895352323356e-08 + Rbc(-2,3) = -2.558080358684755e-07 Zbs(-2,3) = -7.963344725527261e-08 Rbs(-2,3) = 8.664557931997626e-07 Zbc(-2,3) = -1.174845769371228e-07 + Rbc(-1,3) = 5.411513325549776e-06 Zbs(-1,3) = -6.487949016935553e-06 Rbs(-1,3) = 1.632778657338694e-06 Zbc(-1,3) = -7.714055788076447e-06 + Rbc(0,3) = 1.074432554805728e-05 Zbs(0,3) = 9.021685520295440e-05 Rbs(0,3) = -2.670168487324044e-04 Zbc(0,3) = -3.030029084163711e-04 + Rbc(1,3) = -1.026509085983511e-02 Zbs(1,3) = 1.026371228768204e-02 Rbs(1,3) = 2.669471160062606e-03 Zbc(1,3) = 2.491439636469530e-03 + Rbc(2,3) = 1.807615217507021e-03 Zbs(2,3) = 1.755806992704813e-03 Rbs(2,3) = -8.265793546009188e-04 Zbc(2,3) = 1.022998426840788e-03 + Rbc(3,3) = -4.695965106387879e-05 Zbs(3,3) = -5.435431802115176e-05 Rbs(3,3) = -9.610644168956076e-05 Zbc(3,3) = 9.295497835574099e-05 + Rbc(4,3) = -4.259745012678309e-06 Zbs(4,3) = -3.995359587680057e-06 Rbs(4,3) = 3.372029098760316e-06 Zbc(4,3) = -3.495349353958198e-06 + Rbc(5,3) = 1.969099033211810e-07 Zbs(5,3) = 2.454060071425630e-07 Rbs(5,3) = 2.605189612769241e-07 Zbc(5,3) = -2.701555457246008e-07 + Rbc(6,3) = 2.068814490979637e-08 Zbs(6,3) = 2.998317135461603e-08 Rbs(6,3) = -5.798154869460117e-09 Zbc(6,3) = 1.400040655458935e-09 + Rbc(7,3) = 6.588528537632743e-11 Zbs(7,3) = 1.664878885948544e-09 Rbs(7,3) = 5.873097525195203e-11 Zbc(7,3) = 1.044244321863107e-10 + Rbc(8,3) = -7.846676527620357e-12 Zbs(8,3) = 4.242941702954007e-10 Rbs(8,3) = 2.296905043987267e-10 Zbc(8,3) = 3.840700107402091e-10 + Rbc(-8,4) = 2.710041699480554e-11 Zbs(-8,4) = -9.902225290713535e-12 Rbs(-8,4) = -2.238470799595647e-11 Zbc(-8,4) = -2.883055125461924e-11 + Rbc(-7,4) = -7.090863520678464e-11 Zbs(-7,4) = 7.208937980346747e-11 Rbs(-7,4) = 1.360753310088329e-10 Zbc(-7,4) = 1.394975474036972e-10 + Rbc(-6,4) = 9.140125274322566e-12 Zbs(-6,4) = 5.910709211380331e-12 Rbs(-6,4) = 1.376058107839175e-10 Zbc(-6,4) = 1.920353555771325e-10 + Rbc(-5,4) = -1.287811478435119e-10 Zbs(-5,4) = 1.358208747970191e-12 Rbs(-5,4) = 4.195303281794048e-10 Zbc(-5,4) = 3.882622226938529e-10 + Rbc(-4,4) = -1.401813295591428e-08 Zbs(-4,4) = 1.492619012644721e-08 Rbs(-4,4) = -6.658927035325521e-09 Zbc(-4,4) = -5.364315034516646e-09 + Rbc(-3,4) = -1.087231098066446e-08 Zbs(-3,4) = 1.505751083785851e-08 Rbs(-3,4) = 1.979896470172222e-07 Zbc(-3,4) = 1.980363380263228e-07 + Rbc(-2,4) = 8.761150190953027e-07 Zbs(-2,4) = -8.457056403843486e-07 Rbs(-2,4) = 1.843472740187274e-07 Zbc(-2,4) = 3.161584423893988e-07 + Rbc(-1,4) = 1.436645008915578e-05 Zbs(-1,4) = -1.618513247298195e-05 Rbs(-1,4) = 4.733081890999666e-06 Zbc(-1,4) = 4.544048499634805e-06 + Rbc(0,4) = 1.968854570544691e-04 Zbs(0,4) = -2.323016389606880e-04 Rbs(0,4) = -1.517345871040889e-04 Zbc(0,4) = -1.551498051652837e-04 + Rbc(1,4) = -6.393619538512870e-05 Zbs(1,4) = -5.029362074848695e-05 Rbs(1,4) = 1.382575176615545e-04 Zbc(1,4) = 2.050438204199364e-05 + Rbc(2,4) = 5.809246184394435e-05 Zbs(2,4) = -2.685050983087061e-05 Rbs(2,4) = -4.915591334450865e-05 Zbc(2,4) = 5.868516293488592e-06 + Rbc(3,4) = -1.724779759015569e-05 Zbs(3,4) = -1.175566441470858e-05 Rbs(3,4) = 5.292790613473838e-06 Zbc(3,4) = -1.783477435698734e-05 + Rbc(4,4) = 2.138939102198504e-06 Zbs(4,4) = 1.914616755664016e-06 Rbs(4,4) = 2.470759036616283e-06 Zbc(4,4) = -2.269604378372281e-06 + Rbc(5,4) = 1.511333241224931e-07 Zbs(5,4) = -5.240298032596420e-09 Rbs(5,4) = -1.392351275354565e-07 Zbc(5,4) = 1.044085172312984e-07 + Rbc(6,4) = -1.297010378254632e-08 Zbs(6,4) = -3.294502655379767e-08 Rbs(6,4) = 1.840269644941497e-09 Zbc(6,4) = -1.632480196795177e-08 + Rbc(7,4) = -1.716748638304981e-09 Zbs(7,4) = -2.152875047493353e-09 Rbs(7,4) = 3.853045956743605e-09 Zbc(7,4) = -4.361308738488131e-09 + Rbc(8,4) = 7.193104112525001e-11 Zbs(8,4) = 2.319089421525641e-10 Rbs(8,4) = 2.545251533138524e-11 Zbc(8,4) = -3.465852969939852e-10 + Rbc(-8,5) = -3.898832985219361e-12 Zbs(-8,5) = 1.997439817496616e-12 Rbs(-8,5) = -5.889636905807223e-12 Zbc(-8,5) = 3.985821929192387e-12 + Rbc(-7,5) = -4.513270117718036e-11 Zbs(-7,5) = 3.608486460714887e-11 Rbs(-7,5) = -3.653301660163329e-12 Zbc(-7,5) = -9.697402544107292e-12 + Rbc(-6,5) = -2.308214817553468e-11 Zbs(-6,5) = 4.977240220636815e-11 Rbs(-6,5) = -4.387481221019816e-11 Zbc(-6,5) = -5.361976883665739e-11 + Rbc(-5,5) = -2.809000179746720e-10 Zbs(-5,5) = 2.928166180778536e-10 Rbs(-5,5) = 1.766590400910796e-10 Zbc(-5,5) = 1.586354055076726e-10 + Rbc(-4,5) = -2.406056397704874e-09 Zbs(-4,5) = 2.299896704860056e-09 Rbs(-4,5) = -6.444893490388545e-09 Zbc(-4,5) = -6.597315625151220e-09 + Rbc(-3,5) = -7.142888873542924e-08 Zbs(-3,5) = 7.497529611726418e-08 Rbs(-3,5) = 7.377123947163246e-08 Zbc(-3,5) = 6.834213878407007e-08 + Rbc(-2,5) = -4.123561970398102e-07 Zbs(-2,5) = 4.547296893150378e-07 Rbs(-2,5) = 4.469119333549541e-07 Zbc(-2,5) = 4.973753423643017e-07 + Rbc(-1,5) = -8.466146980491286e-07 Zbs(-1,5) = 1.222910545623143e-06 Rbs(-1,5) = 6.766638035709763e-06 Zbc(-1,5) = 7.194924482862005e-06 + Rbc(0,5) = 5.425661295796407e-05 Zbs(0,5) = -5.325617858722969e-05 Rbs(0,5) = 1.265427750164829e-05 Zbc(0,5) = 1.803245240949694e-05 + Rbc(1,5) = -2.419655333032779e-05 Zbs(1,5) = -1.017028722666939e-05 Rbs(1,5) = 7.306095441788181e-05 Zbc(1,5) = 7.314756621304179e-05 + Rbc(2,5) = 5.101038569308111e-04 Zbs(2,5) = -5.124903630344561e-04 Rbs(2,5) = -2.632264215783870e-04 Zbc(2,5) = -2.266875295370781e-04 + Rbc(3,5) = -1.007614308017720e-04 Zbs(3,5) = -7.380010276383178e-05 Rbs(3,5) = 4.379235593354428e-05 Zbc(3,5) = -9.285826334256841e-05 + Rbc(4,5) = 5.944300041616924e-06 Zbs(4,5) = 7.222184858568008e-06 Rbs(4,5) = 9.251701284925513e-06 Zbc(4,5) = -6.363837109955947e-06 + Rbc(5,5) = 3.757833340012013e-07 Zbs(5,5) = 1.851119825069280e-07 Rbs(5,5) = -5.059847464245063e-07 Zbc(5,5) = 7.702028729623193e-07 + Rbc(6,5) = -4.441630861138737e-08 Zbs(6,5) = -7.368665295727156e-08 Rbs(6,5) = -2.775889499521497e-08 Zbc(6,5) = 3.449679104827633e-08 + Rbc(7,5) = -5.060272656702438e-09 Zbs(7,5) = -5.776024109288837e-09 Rbs(7,5) = 3.147865973746203e-09 Zbc(7,5) = -1.976812631368815e-09 + Rbc(8,5) = 9.390314091066755e-11 Zbs(8,5) = -1.198604593069260e-10 Rbs(8,5) = 9.841884302829231e-11 Zbc(8,5) = -1.256289714637018e-10 + Rbc(-8,6) = 6.466062700579490e-12 Zbs(-8,6) = -1.183839393335184e-11 Rbs(-8,6) = 4.524911655785481e-12 Zbc(-8,6) = 1.476242703638775e-12 + Rbc(-7,6) = 2.096316462382459e-12 Zbs(-7,6) = -3.619452052216971e-12 Rbs(-7,6) = -2.321450185529311e-12 Zbc(-7,6) = -7.416910828824755e-12 + Rbc(-6,6) = 4.920200018830704e-12 Zbs(-6,6) = -6.020213923128178e-13 Rbs(-6,6) = -1.261738746636541e-11 Zbc(-6,6) = -8.629012523408703e-12 + Rbc(-5,6) = -2.673141177817674e-11 Zbs(-5,6) = 1.205741978218008e-11 Rbs(-5,6) = 6.141799747456691e-11 Zbc(-5,6) = 6.531106152281628e-11 + Rbc(-4,6) = 4.346440594165293e-10 Zbs(-4,6) = -5.932845466114468e-10 Rbs(-4,6) = -7.928856766510231e-10 Zbc(-4,6) = -7.352168948761655e-10 + Rbc(-3,6) = 1.976658881522078e-09 Zbs(-3,6) = -2.551903429673480e-09 Rbs(-3,6) = 1.914661956903727e-09 Zbc(-3,6) = -3.447548475402002e-10 + Rbc(-2,6) = 2.679593747801372e-08 Zbs(-2,6) = -2.810195514094542e-08 Rbs(-2,6) = -7.074228759106105e-08 Zbc(-2,6) = -7.957117663766914e-08 + Rbc(-1,6) = -8.995579950390309e-07 Zbs(-1,6) = 9.304788375651144e-07 Rbs(-1,6) = -3.149400596192336e-07 Zbc(-1,6) = -4.357174798479014e-07 + Rbc(0,6) = -1.612406838928942e-05 Zbs(0,6) = 1.718756727559504e-05 Rbs(0,6) = 7.652862153456316e-07 Zbc(0,6) = 8.181964675686984e-07 + Rbc(1,6) = -2.881796828532072e-05 Zbs(1,6) = 3.911580417222984e-05 Rbs(1,6) = 3.238259191387741e-05 Zbc(1,6) = 3.513966148529251e-05 + Rbc(2,6) = 2.934810459471068e-05 Zbs(2,6) = -1.549768861910624e-05 Rbs(2,6) = -3.390214520878502e-05 Zbc(2,6) = -1.214577986747784e-05 + Rbc(3,6) = -1.211020488284986e-05 Zbs(3,6) = 5.492440816493984e-07 Rbs(3,6) = 1.295444928553493e-05 Zbc(3,6) = -2.246232226061086e-06 + Rbc(4,6) = 2.821275203014526e-06 Zbs(4,6) = 8.541439159627405e-07 Rbs(4,6) = -5.665276031513345e-07 Zbc(4,6) = 2.705386029705306e-06 + Rbc(5,6) = -4.044337200196656e-07 Zbs(5,6) = -4.316783840104614e-07 Rbs(5,6) = -3.408109082089906e-07 Zbc(5,6) = 2.677725862109344e-07 + Rbc(6,6) = -2.285148626898629e-08 Zbs(6,6) = -6.031858504500824e-09 Rbs(6,6) = 4.020993130179712e-08 Zbc(6,6) = -2.539620012995041e-08 + Rbc(7,6) = 1.921239642791208e-09 Zbs(7,6) = 4.028413238799621e-09 Rbs(7,6) = 4.069407827530441e-10 Zbc(7,6) = 3.851845587879371e-09 + Rbc(8,6) = 1.113264867636701e-10 Zbs(8,6) = 7.414160795004551e-12 Rbs(8,6) = -6.036235032821986e-10 Zbc(8,6) = 7.890200247166661e-10 + Rbc(-8,7) = 5.611578953064622e-13 Zbs(-8,7) = 3.130404581599155e-12 Rbs(-8,7) = -3.211089655855773e-12 Zbc(-8,7) = -6.426258230910623e-12 + Rbc(-7,7) = -3.012106034236786e-12 Zbs(-7,7) = 6.217532389355310e-12 Rbs(-7,7) = 2.704090358063821e-12 Zbc(-7,7) = 5.424354476018265e-12 + Rbc(-6,7) = 4.631295578811580e-12 Zbs(-6,7) = -1.011188744892614e-11 Rbs(-6,7) = 3.127672229287207e-12 Zbc(-6,7) = 6.625800036443396e-12 + Rbc(-5,7) = -8.451886930076989e-12 Zbs(-5,7) = 7.246357022582610e-12 Rbs(-5,7) = 8.246073443074822e-12 Zbc(-5,7) = 2.278310937319792e-12 + Rbc(-4,7) = -1.599931778840391e-10 Zbs(-4,7) = 1.644200735201037e-10 Rbs(-4,7) = 1.376674032737270e-10 Zbc(-4,7) = 2.057495137843461e-10 + Rbc(-3,7) = 1.592693547241850e-09 Zbs(-3,7) = -1.871573320097678e-09 Rbs(-3,7) = 2.737894339357076e-09 Zbc(-3,7) = 2.986572858084550e-09 + Rbc(-2,7) = 1.949815987746918e-08 Zbs(-2,7) = -2.383200071360591e-08 Rbs(-2,7) = -7.922759290010990e-09 Zbc(-2,7) = -6.290660232943728e-09 + Rbc(-1,7) = 2.124958429820849e-07 Zbs(-1,7) = -2.462740005737867e-07 Rbs(-1,7) = -1.290285304282255e-07 Zbc(-1,7) = -1.522775112847011e-07 + Rbc(0,7) = 3.019564859247620e-07 Zbs(0,7) = -4.903196597783117e-07 Rbs(0,7) = -2.003167932609825e-06 Zbc(0,7) = -2.249574904433446e-06 + Rbc(1,7) = -1.006795470617991e-05 Zbs(1,7) = 9.870933822512273e-06 Rbs(1,7) = 1.531638431795546e-06 Zbc(1,7) = 8.169977836976633e-08 + Rbc(2,7) = 2.246168966170586e-06 Zbs(2,7) = 2.716600497655771e-06 Rbs(2,7) = -1.114240856426433e-05 Zbc(2,7) = -9.151883407518896e-06 + Rbc(3,7) = -2.697171368009640e-05 Zbs(3,7) = 2.792707200524424e-05 Rbs(3,7) = 2.134675251264458e-05 Zbc(3,7) = 1.665014779928071e-05 + Rbc(4,7) = 6.335284499697205e-06 Zbs(4,7) = 2.861969226894860e-06 Rbs(4,7) = -2.564557697926165e-06 Zbc(4,7) = 7.029603038046887e-06 + Rbc(5,7) = -6.397740541679363e-07 Zbs(5,7) = -6.311718814208943e-07 Rbs(5,7) = -7.592239419772196e-07 Zbc(5,7) = 3.420822956150132e-07 + Rbc(6,7) = -2.280437163463908e-08 Zbs(6,7) = 2.072560589414034e-08 Rbs(6,7) = 6.069751903304861e-08 Zbc(6,7) = -7.672107883581729e-08 + Rbc(7,7) = 5.384292122308359e-09 Zbs(7,7) = 1.032613397081111e-08 Rbs(7,7) = 2.431945566644280e-10 Zbc(7,7) = 1.185606926153265e-09 + Rbc(8,7) = 4.243400949419477e-10 Zbs(8,7) = 4.879132048856139e-10 Rbs(8,7) = -5.689422439772297e-10 Zbc(8,7) = 6.070833107108635e-10 + Rbc(-8,8) = -2.574576023346106e-12 Zbs(-8,8) = 2.768290164346279e-12 Rbs(-8,8) = -2.630741814700850e-13 Zbc(-8,8) = 3.422829688902942e-12 + Rbc(-7,8) = -5.321969262672291e-13 Zbs(-7,8) = -1.422794907272397e-12 Rbs(-7,8) = 2.001181065593549e-12 Zbc(-7,8) = 3.256110652079520e-12 + Rbc(-6,8) = 2.827360642102881e-12 Zbs(-6,8) = -1.858003186830016e-12 Rbs(-6,8) = -2.370681707513467e-12 Zbc(-6,8) = -3.416212342238121e-12 + Rbc(-5,8) = -5.644426029841578e-12 Zbs(-5,8) = 7.682159625406356e-12 Rbs(-5,8) = -8.250604970968244e-12 Zbc(-5,8) = -7.479174793331905e-12 + Rbc(-4,8) = -1.256266454448778e-10 Zbs(-4,8) = 1.334285354218545e-10 Rbs(-4,8) = -3.203105812184868e-11 Zbc(-4,8) = -3.714827893614781e-11 + Rbc(-3,8) = -4.008633146884750e-10 Zbs(-3,8) = 5.799121826355525e-10 Rbs(-3,8) = 1.028971454308245e-09 Zbc(-3,8) = 1.072241943406915e-09 + Rbc(-2,8) = -2.251737402008495e-09 Zbs(-2,8) = 2.925001290941927e-09 Rbs(-2,8) = 5.068868568856430e-09 Zbc(-2,8) = 6.682687701203482e-09 + Rbc(-1,8) = 2.197995768349787e-08 Zbs(-1,8) = -2.256230819620221e-08 Rbs(-1,8) = 6.224043765622159e-08 Zbc(-1,8) = 7.356856306201924e-08 + Rbc(0,8) = 4.122908776382375e-07 Zbs(0,8) = -4.786477401586018e-07 Rbs(0,8) = -1.463088141943764e-09 Zbc(0,8) = 5.580426240227131e-08 + Rbc(1,8) = 2.968618812495961e-06 Zbs(1,8) = -3.278382233320496e-06 Rbs(1,8) = -5.471126121790955e-07 Zbc(1,8) = -6.209925761073740e-07 + Rbc(2,8) = 2.711899543500591e-06 Zbs(2,8) = -4.226143777025681e-06 Rbs(2,8) = -5.204973819888417e-06 Zbc(2,8) = -5.804487694017731e-06 + Rbc(3,8) = -4.187259475774294e-06 Zbs(3,8) = 3.115214796379499e-06 Rbs(3,8) = 5.986185521826896e-06 Zbc(3,8) = 2.820321963394401e-06 + Rbc(4,8) = 1.591633665163278e-06 Zbs(4,8) = -9.012547435367010e-08 Rbs(4,8) = -2.032213945357463e-06 Zbc(4,8) = 5.508242131517138e-07 + Rbc(5,8) = -4.272167701060337e-07 Zbs(5,8) = -4.638918501540492e-08 Rbs(5,8) = 6.555152681292947e-08 Zbc(5,8) = -3.258760552611915e-07 + Rbc(6,8) = 5.932961664145535e-08 Zbs(6,8) = 6.877699679748886e-08 Rbs(6,8) = 4.529144354917304e-08 Zbc(6,8) = -1.937717612133139e-08 + Rbc(7,8) = 2.493875347568174e-09 Zbs(7,8) = 7.098923485011453e-10 Rbs(7,8) = -7.561407557676655e-09 Zbc(7,8) = 5.675659583716489e-09 + Rbc(8,8) = -4.299028791947558e-10 Zbs(8,8) = -4.941554633908124e-10 Rbs(8,8) = -5.585495017921668e-11 Zbc(8,8) = -4.969392042552561e-10 + Rbc(-8,9) = 9.404154306251565e-13 Zbs(-8,9) = -2.959379152313779e-12 Rbs(-8,9) = 1.899739731072267e-12 Zbc(-8,9) = 1.191270899448973e-12 + Rbc(-7,9) = 7.823038021875159e-13 Zbs(-7,9) = -1.511794061584066e-12 Rbs(-7,9) = -1.156333539273618e-12 Zbc(-7,9) = -2.911027426209155e-12 + Rbc(-6,9) = -2.198121405703133e-12 Zbs(-6,9) = 4.347415053231293e-12 Rbs(-6,9) = -6.984352433400718e-13 Zbc(-6,9) = -1.064298661623986e-12 + Rbc(-5,9) = 2.285075992065686e-12 Zbs(-5,9) = -2.738201341562933e-12 Rbs(-5,9) = 1.983938772055576e-12 Zbc(-5,9) = 4.263200146411232e-12 + Rbc(-4,9) = 2.362145535399473e-12 Zbs(-4,9) = -9.103270446099149e-12 Rbs(-4,9) = -3.938574625967168e-11 Zbc(-4,9) = -4.423692834960295e-11 + Rbc(-3,9) = -1.227566445191145e-10 Zbs(-3,9) = 1.328927960155367e-10 Rbs(-3,9) = -1.520799485566943e-10 Zbc(-3,9) = -2.062502986867317e-10 + Rbc(-2,9) = -2.273923168837524e-09 Zbs(-2,9) = 2.448670220398621e-09 Rbs(-2,9) = -1.048164309441880e-09 Zbc(-2,9) = -1.273861029785746e-09 + Rbc(-1,9) = -1.727395012356134e-08 Zbs(-1,9) = 2.047747849995752e-08 Rbs(-1,9) = 8.904890067257325e-09 Zbc(-1,9) = 8.215632588116670e-09 + Rbc(0,9) = -7.383469663551062e-08 Zbs(0,9) = 9.414804558607687e-08 Rbs(0,9) = 9.322539407142755e-08 Zbc(0,9) = 1.067154217911835e-07 + Rbc(1,9) = -3.763944352851072e-08 Zbs(1,9) = 8.198742609319860e-08 Rbs(1,9) = 4.308418002171212e-07 Zbc(1,9) = 5.190934984707110e-07 + Rbc(2,9) = 1.207061511778393e-06 Zbs(2,9) = -1.225882250540692e-06 Rbs(2,9) = -8.111960463465227e-07 Zbc(2,9) = -5.243435010296794e-07 + Rbc(3,9) = -9.022896070983054e-08 Zbs(3,9) = -4.138240421857809e-07 Rbs(3,9) = 1.458102216198225e-06 Zbc(3,9) = 9.325916210238820e-07 + Rbc(4,9) = 1.457641699054854e-06 Zbs(4,9) = -1.613434022041675e-06 Rbs(4,9) = -1.623177248209507e-06 Zbc(4,9) = -1.072465690785483e-06 + Rbc(5,9) = -4.331989697081155e-07 Zbs(5,9) = -8.529760521617342e-08 Rbs(5,9) = 1.560695519576881e-07 Zbc(5,9) = -4.997540456262304e-07 + Rbc(6,9) = 6.406104445964064e-08 Zbs(6,9) = 4.755320313242537e-08 Rbs(6,9) = 5.686544405460106e-08 Zbc(6,9) = -1.625427445207543e-08 + Rbc(7,9) = 3.142861388138564e-10 Zbs(7,9) = -4.688770756300245e-09 Rbs(7,9) = -6.676724097092652e-09 Zbc(7,9) = 5.118909524845958e-09 + Rbc(8,9) = -4.953587354772186e-10 Zbs(8,9) = -8.454078957388048e-10 Rbs(8,9) = 3.419920885099806e-10 Zbc(8,9) = -6.753818016726443e-10 + Rbc(-8,10) = 5.267829163127372e-13 Zbs(-8,10) = 4.399092138030744e-13 Rbs(-8,10) = -1.271291989845375e-12 Zbc(-8,10) = -2.432074035242944e-12 + Rbc(-7,10) = -4.886395842243438e-13 Zbs(-7,10) = 1.767648543411968e-12 Rbs(-7,10) = -3.544819216082360e-13 Zbc(-7,10) = -1.387828979478368e-13 + Rbc(-6,10) = -2.645894478948478e-14 Zbs(-6,10) = -1.441496713208009e-12 Rbs(-6,10) = 2.665084044360851e-13 Zbc(-6,10) = 1.131792987698176e-12 + Rbc(-5,10) = 5.588869747306412e-13 Zbs(-5,10) = -1.289896346597976e-12 Rbs(-5,10) = 5.589986243163118e-13 Zbc(-5,10) = -1.716628709206437e-12 + Rbc(-4,10) = 4.856541502645481e-12 Zbs(-4,10) = -2.801282668794016e-12 Rbs(-4,10) = 4.666709610270481e-12 Zbc(-4,10) = 6.135041392090372e-12 + Rbc(-3,10) = 9.636496446325965e-11 Zbs(-3,10) = -1.091293150183128e-10 Rbs(-3,10) = -1.121181520680161e-11 Zbc(-3,10) = -3.531965285184967e-12 + Rbc(-2,10) = 4.677772191864018e-10 Zbs(-2,10) = -5.881282866269906e-10 Rbs(-2,10) = -6.885646562824077e-10 Zbc(-2,10) = -7.381409056108407e-10 + Rbc(-1,10) = -5.196679003785474e-11 Zbs(-1,10) = -3.616032436440601e-10 Rbs(-1,10) = -5.667012550137859e-09 Zbc(-1,10) = -6.595697388415431e-09 + Rbc(0,10) = -3.080501846004862e-08 Zbs(0,10) = 3.191818250286566e-08 Rbs(0,10) = -2.080412536330343e-08 Zbc(0,10) = -2.644013199522021e-08 + Rbc(1,10) = -8.799628195370653e-08 Zbs(1,10) = 1.166919528152073e-07 Rbs(1,10) = 2.621877872471209e-08 Zbc(1,10) = 1.514304678394177e-08 + Rbc(2,10) = -3.558084046396312e-07 Zbs(2,10) = 4.124430265857929e-07 Rbs(2,10) = 8.280620537973879e-08 Zbc(2,10) = 1.146594329744486e-07 + Rbc(3,10) = -1.596834944364167e-07 Zbs(3,10) = 3.279831869225101e-07 Rbs(3,10) = 7.209510341738255e-07 Zbc(3,10) = 7.910840483194865e-07 + Rbc(4,10) = 4.053459851503233e-07 Zbs(4,10) = -3.751296573634738e-07 Rbs(4,10) = -8.346465163581559e-07 Zbc(4,10) = -4.373985138172283e-07 + Rbc(5,10) = -1.741089004724236e-07 Zbs(5,10) = 3.801045484540719e-08 Rbs(5,10) = 2.547003415735843e-07 Zbc(5,10) = -8.669880665201994e-08 + Rbc(6,10) = 5.709092297877517e-08 Zbs(6,10) = 1.829378694310391e-09 Rbs(6,10) = -8.470936008672115e-09 Zbc(6,10) = 3.548775507430536e-08 + Rbc(7,10) = -7.803389589558419e-09 Zbs(7,10) = -8.771836495329159e-09 Rbs(7,10) = -5.742513875898595e-09 Zbc(7,10) = 4.923469901532169e-10 + Rbc(8,10) = -2.210990582816217e-10 Zbs(8,10) = 6.741007268641609e-11 Rbs(8,10) = 1.107094283743895e-09 Zbc(8,10) = -8.942272145616133e-10 + Rbc(-8,11) = -8.450698096769665e-13 Zbs(-8,11) = 1.228455733985704e-12 Rbs(-8,11) = -1.384960183228104e-13 Zbc(-8,11) = 8.846737212802603e-13 + Rbc(-7,11) = -1.829869947335953e-13 Zbs(-7,11) = -3.436647818556341e-15 Rbs(-7,11) = 6.642581032526250e-13 Zbc(-7,11) = 1.329122954947339e-12 + Rbc(-6,11) = 1.206600194873222e-12 Zbs(-6,11) = -1.075613259205626e-12 Rbs(-6,11) = -1.439049126729879e-14 Zbc(-6,11) = -2.596693975453555e-13 + Rbc(-5,11) = -5.334149977000536e-13 Zbs(-5,11) = 1.066324363919938e-12 Rbs(-5,11) = -1.400174026601464e-12 Zbc(-5,11) = -1.037009822117576e-12 + Rbc(-4,11) = -3.914922385363240e-12 Zbs(-4,11) = 4.012718832367316e-12 Rbs(-4,11) = 2.663492981369112e-12 Zbc(-4,11) = 3.278939629519079e-12 + Rbc(-3,11) = 1.251506968153272e-12 Zbs(-3,11) = 1.244126728819490e-12 Rbs(-3,11) = 2.428754832810459e-11 Zbc(-3,11) = 2.708286649831066e-11 + Rbc(-2,11) = 5.914716761991593e-11 Zbs(-2,11) = -6.095802533206408e-11 Rbs(-2,11) = 1.329977651446009e-10 Zbc(-2,11) = 1.682776541349133e-10 + Rbc(-1,11) = 1.455432526902963e-09 Zbs(-1,11) = -1.600760116006733e-09 Rbs(-1,11) = 4.268025995598114e-10 Zbc(-1,11) = 5.994794615174278e-10 + Rbc(0,11) = 8.158781121785977e-09 Zbs(0,11) = -9.742759171368459e-09 Rbs(0,11) = -4.926897299974606e-09 Zbc(0,11) = -4.964397533863890e-09 + Rbc(1,11) = 1.353558367594108e-08 Zbs(1,11) = -1.962917635281143e-08 Rbs(1,11) = -2.635467225692176e-08 Zbc(1,11) = -3.159855539314893e-08 + Rbc(2,11) = -1.410603309913236e-08 Zbs(2,11) = 7.880424695112289e-09 Rbs(2,11) = -7.268491270290704e-08 Zbc(2,11) = -9.216275852556151e-08 + Rbc(3,11) = -1.021367975050433e-07 Zbs(3,11) = 1.198302431563501e-07 Rbs(3,11) = 1.708972361334600e-07 Zbc(3,11) = 1.276441381989995e-07 + Rbc(4,11) = -8.038887641482993e-09 Zbs(4,11) = 4.178111115077605e-08 Rbs(4,11) = -1.869954240217108e-07 Zbc(4,11) = -9.486177283048781e-08 + Rbc(5,11) = -8.338491050338180e-08 Zbs(5,11) = 1.029920977288049e-07 Rbs(5,11) = 1.275045512262688e-07 Zbc(5,11) = 5.997333788277689e-08 + Rbc(6,11) = 3.304578721230144e-08 Zbs(6,11) = 4.129038770792757e-10 Rbs(6,11) = -1.112740198659094e-08 Zbc(6,11) = 3.529034857148920e-08 + Rbc(7,11) = -6.355394248495517e-09 Zbs(7,11) = -3.450212070574222e-09 Rbs(7,11) = -3.914362830192705e-09 Zbc(7,11) = 5.966354356385917e-10 + Rbc(8,11) = 2.081192941755067e-10 Zbs(8,11) = 5.558235847139504e-10 Rbs(8,11) = 6.754928495821062e-10 Zbc(8,11) = -2.052902938517221e-10 + Rbc(-8,12) = 2.316611770224732e-13 Zbs(-8,12) = -7.955754825437283e-13 Rbs(-8,12) = 6.890259240883769e-13 Zbc(-8,12) = 4.907549168659864e-13 + Rbc(-7,12) = 3.472306858242482e-13 Zbs(-7,12) = -7.786895688445435e-13 Rbs(-7,12) = -2.553720627512086e-13 Zbc(-7,12) = -5.006031202590202e-13 + Rbc(-6,12) = -8.184821427150468e-13 Zbs(-6,12) = 1.064605993890629e-12 Rbs(-6,12) = -2.181809938436523e-13 Zbc(-6,12) = -5.528486987002861e-13 + Rbc(-5,12) = -2.067182880866850e-14 Zbs(-5,12) = 2.587209603091814e-13 Rbs(-5,12) = 9.816583809700851e-13 Zbc(-5,12) = 1.443074537247692e-12 + Rbc(-4,12) = 7.061022008280015e-13 Zbs(-4,12) = -1.608537644873556e-12 Rbs(-4,12) = -1.125888931336082e-12 Zbc(-4,12) = -1.285454315560784e-12 + Rbc(-3,12) = -6.193727952195699e-12 Zbs(-3,12) = 7.535965405483275e-12 Rbs(-3,12) = -4.121790587585987e-12 Zbc(-3,12) = -6.141828163767011e-12 + Rbc(-2,12) = -5.915850434066642e-11 Zbs(-2,12) = 6.883380542317813e-11 Rbs(-2,12) = 1.524445312254795e-11 Zbc(-2,12) = 1.643665837760510e-11 + Rbc(-1,12) = -2.106152099827490e-10 Zbs(-1,12) = 2.686099753432962e-10 Rbs(-1,12) = 3.759816013174261e-10 Zbc(-1,12) = 4.164915655102116e-10 + Rbc(0,12) = 4.340911540009209e-10 Zbs(0,12) = -2.810434691938696e-10 Rbs(0,12) = 2.171625413604578e-09 Zbc(0,12) = 2.609368754820512e-09 + Rbc(1,12) = 1.008417684113132e-08 Zbs(1,12) = -1.056162973680574e-08 Rbs(1,12) = 2.862758204512754e-09 Zbc(1,12) = 4.582618615496998e-09 + Rbc(2,12) = 1.234382763181695e-08 Zbs(2,12) = -1.933672341102642e-08 Rbs(2,12) = -1.016864310591520e-08 Zbc(2,12) = -9.431883597086025e-09 + Rbc(3,12) = 3.616207688262134e-08 Zbs(3,12) = -4.309708317764294e-08 Rbs(3,12) = -1.980698346472854e-09 Zbc(3,12) = -1.060317022969533e-08 + Rbc(4,12) = -3.067711086725851e-09 Zbs(4,12) = -1.438301432038085e-08 Rbs(4,12) = -8.998358471131711e-08 Zbc(4,12) = -9.335983897561073e-08 + Rbc(5,12) = -2.925990667700169e-08 Zbs(5,12) = 3.672075303928347e-08 Rbs(5,12) = 9.899401255660774e-08 Zbc(5,12) = 5.423710629002679e-08 + Rbc(6,12) = 1.750212854074719e-08 Zbs(6,12) = -8.033641036643083e-09 Rbs(6,12) = -2.829887442918312e-08 Zbc(6,12) = 1.037639033916627e-08 + Rbc(7,12) = -6.894070589121807e-09 Zbs(7,12) = -9.858751937424598e-12 Rbs(7,12) = 1.152832213491546e-09 Zbc(7,12) = -3.728803261803748e-09 + Rbc(8,12) = 9.294824432633004e-10 Zbs(8,12) = 9.542968406307855e-10 Rbs(8,12) = 6.433742038489872e-10 Zbc(8,12) = 8.330254779084399e-11 + Rbc(-8,13) = 3.088948040092467e-13 Zbs(-8,13) = -5.361089514323115e-14 Rbs(-8,13) = -3.532592926259321e-13 Zbc(-8,13) = -6.832814870117882e-13 + Rbc(-7,13) = -2.453513800550679e-15 Zbs(-7,13) = 3.611432236470862e-13 Rbs(-7,13) = -1.853999144920627e-13 Zbc(-7,13) = -2.634134590163917e-13 + Rbc(-6,13) = -2.480527556228256e-14 Zbs(-6,13) = -2.520118643310057e-13 Rbs(-6,13) = 1.220728238815877e-13 Zbc(-6,13) = 3.390384128087301e-13 + Rbc(-5,13) = 2.851681634225399e-13 Zbs(-5,13) = -4.610111511311642e-13 Rbs(-5,13) = 2.483693723272079e-14 Zbc(-5,13) = -4.717133990639581e-13 + Rbc(-4,13) = 3.279453503559181e-13 Zbs(-4,13) = 3.141354964522745e-13 Rbs(-4,13) = -3.765518543920631e-13 Zbc(-4,13) = -2.985884358052631e-13 + Rbc(-3,13) = 1.164385160894161e-12 Zbs(-3,13) = -1.961816611926657e-12 Rbs(-3,13) = -1.450517099921038e-12 Zbc(-3,13) = -7.798871825079524e-13 + Rbc(-2,13) = 6.547836902859316e-13 Zbs(-2,13) = -2.860466352828818e-12 Rbs(-2,13) = -1.574502659477138e-11 Zbc(-2,13) = -1.982340588190799e-11 + Rbc(-1,13) = -6.225747874589125e-11 Zbs(-1,13) = 7.326817709068575e-11 Rbs(-1,13) = -7.879988053331628e-11 Zbc(-1,13) = -9.836413542301382e-11 + Rbc(0,13) = -6.092777617014294e-10 Zbs(0,13) = 6.952831589219671e-10 Rbs(0,13) = 1.355355362687062e-11 Zbc(0,13) = -1.564602606212603e-11 + Rbc(1,13) = -1.950589606354981e-09 Zbs(1,13) = 2.347232424584388e-09 Rbs(1,13) = 1.624336702589809e-09 Zbc(1,13) = 1.763372784224582e-09 + Rbc(2,13) = -1.658674069400300e-09 Zbs(2,13) = 2.662420745354741e-09 Rbs(2,13) = 4.813685828472461e-09 Zbc(2,13) = 6.134946373340419e-09 + Rbc(3,13) = 6.206603612927964e-09 Zbs(3,13) = -5.683298604088747e-09 Rbs(3,13) = 9.801274586618527e-09 Zbc(3,13) = 1.284884243977966e-08 + Rbc(4,13) = 3.810246405351359e-09 Zbs(4,13) = -8.481183756836699e-09 Rbs(4,13) = -2.679307688165105e-08 Zbc(4,13) = -2.171127811253177e-08 + Rbc(5,13) = 3.250273533922722e-09 Zbs(5,13) = -3.071157954597484e-09 Rbs(5,13) = 2.413758249355239e-08 Zbc(5,13) = 1.107637198073719e-08 + Rbc(6,13) = 5.353104757908876e-09 Zbs(6,13) = -7.862670148323320e-09 Rbs(6,13) = -1.142207286614334e-08 Zbc(6,13) = -2.774636019593553e-09 + Rbc(7,13) = -2.884267852214222e-09 Zbs(7,13) = 2.704689111866611e-10 Rbs(7,13) = 1.135942089402425e-09 Zbc(7,13) = -2.626178920124743e-09 + Rbc(8,13) = 6.281011178855889e-10 Zbs(8,13) = 2.560990284899142e-10 Rbs(8,13) = 2.218781090035581e-10 Zbc(8,13) = -5.735308906933632e-12 + Rbc(-8,14) = -3.042682721452879e-13 Zbs(-8,14) = 3.031209693594724e-13 Rbs(-8,14) = 3.102187106918626e-14 Zbc(-8,14) = 1.446583689858997e-13 + Rbc(-7,14) = -2.603204991491677e-14 Zbs(-7,14) = 2.760847191233080e-13 Rbs(-7,14) = 1.412852773297288e-13 Zbc(-7,14) = 2.362669910317242e-13 + Rbc(-6,14) = 2.684011447687661e-13 Zbs(-6,14) = -1.475261742276385e-13 Rbs(-6,14) = -8.344387947342067e-15 Zbc(-6,14) = 2.139818140941184e-13 + Rbc(-5,14) = -1.421341046424859e-13 Zbs(-5,14) = -9.272460903575860e-14 Rbs(-5,14) = -3.294394134383269e-13 Zbc(-5,14) = -2.390836987882096e-13 + Rbc(-4,14) = -4.818320963398330e-13 Zbs(-4,14) = 4.764053033172068e-13 Rbs(-4,14) = 4.031411539875692e-13 Zbc(-4,14) = 1.567889607618533e-13 + Rbc(-3,14) = 9.484896932670206e-13 Zbs(-3,14) = -6.812503947404806e-13 Rbs(-3,14) = 9.468391388088574e-13 Zbc(-3,14) = 1.100929990803375e-12 + Rbc(-2,14) = 4.467363778318961e-12 Zbs(-2,14) = -4.916651599568247e-12 Rbs(-2,14) = -4.714007277244825e-13 Zbc(-2,14) = 6.674490829610009e-13 + Rbc(-1,14) = 2.504941019587797e-11 Zbs(-1,14) = -3.285198055094256e-11 Rbs(-1,14) = -1.694453651372266e-11 Zbc(-1,14) = -1.286207415954953e-11 + Rbc(0,14) = 5.345145070961818e-11 Zbs(0,14) = -9.762904894010550e-11 Rbs(0,14) = -1.597568212132268e-10 Zbc(0,14) = -1.763742814406020e-10 + Rbc(1,14) = -1.514214204811284e-10 Zbs(1,14) = 1.077071693652093e-10 Rbs(1,14) = -5.709210357207063e-10 Zbc(1,14) = -6.802870420088740e-10 + Rbc(2,14) = -2.104576893544904e-09 Zbs(2,14) = 2.237120728763432e-09 Rbs(2,14) = -1.378282406231595e-11 Zbc(2,14) = -3.893951744090130e-10 + Rbc(3,14) = -1.008290130988519e-09 Zbs(3,14) = 2.219168092141341e-09 Rbs(3,14) = 2.435784510546244e-09 Zbc(3,14) = 2.596310209032110e-09 + Rbc(4,14) = -3.923570194538885e-09 Zbs(4,14) = 4.383084231876902e-09 Rbs(4,14) = -1.682048967815908e-09 Zbc(4,14) = -1.089904234345965e-11 + Rbc(5,14) = 2.728801345856591e-09 Zbs(5,14) = -7.875325964784877e-10 Rbs(5,14) = 1.031466845362387e-08 Zbc(5,14) = 9.926741850486566e-09 + Rbc(6,14) = 1.413717124468599e-09 Zbs(6,14) = -3.238402353507821e-09 Rbs(6,14) = -1.040070031358174e-08 Zbc(6,14) = -5.951639656535716e-09 + Rbc(7,14) = -1.680832499020794e-09 Zbs(7,14) = 1.221603920539884e-09 Rbs(7,14) = 2.885533480603681e-09 Zbc(7,14) = -9.926234325485567e-10 + Rbc(8,14) = 7.214380659829189e-10 Zbs(8,14) = 7.531475316762685e-12 Rbs(8,14) = -1.387410213966795e-10 Zbc(8,14) = 3.596813154659095e-10 + Rbc(-8,15) = 1.300303997709365e-13 Zbs(-8,15) = -1.256590938743755e-13 Rbs(-8,15) = 1.736285299802916e-13 Zbc(-8,15) = 1.735434913351022e-13 + Rbc(-7,15) = 1.607221832557518e-13 Zbs(-7,15) = -2.070196213811446e-13 Rbs(-7,15) = -5.289711602160850e-14 Zbc(-7,15) = -1.599179296467749e-14 + Rbc(-6,15) = -2.082403036224150e-13 Zbs(-6,15) = 1.574269952861465e-13 Rbs(-6,15) = -1.149447392282954e-13 Zbc(-6,15) = -1.400243904278059e-13 + Rbc(-5,15) = -2.764590473006736e-14 Zbs(-5,15) = 8.742683514948912e-14 Rbs(-5,15) = 2.982714884378287e-13 Zbc(-5,15) = 2.725381368682690e-13 + Rbc(-4,15) = 3.685411456613409e-13 Zbs(-4,15) = -3.698582328385557e-13 Rbs(-4,15) = -1.424755801120111e-13 Zbc(-4,15) = -5.867516189257804e-14 + Rbc(-3,15) = -5.267791377782850e-13 Zbs(-3,15) = 4.304269212474405e-13 Rbs(-3,15) = -3.768490668447501e-13 Zbc(-3,15) = -4.278096305180954e-13 + Rbc(-2,15) = -1.094945954261687e-12 Zbs(-2,15) = 1.248982267868940e-12 Rbs(-2,15) = 2.098690308942342e-12 Zbc(-2,15) = 2.010886065838906e-12 + Rbc(-1,15) = 5.445489193697982e-12 Zbs(-1,15) = -5.200018858572045e-12 Rbs(-1,15) = 8.205785777076995e-12 Zbc(-1,15) = 8.776213086732025e-12 + Rbc(0,15) = 3.328889991407975e-11 Zbs(0,15) = -3.485757085827302e-11 Rbs(0,15) = 6.416943754331835e-12 Zbc(0,15) = 8.455332038155741e-12 + Rbc(1,15) = 1.195944944354703e-10 Zbs(1,15) = -1.286907151940970e-10 Rbs(1,15) = -4.754159301297458e-11 Zbc(1,15) = -4.722005473174563e-11 + Rbc(2,15) = 2.589238350847585e-10 Zbs(2,15) = -2.895594948724628e-10 Rbs(2,15) = -3.527613343359772e-10 Zbc(2,15) = -3.711401637607420e-10 + Rbc(3,15) = 1.635491805948442e-10 Zbs(3,15) = -2.198751861846919e-10 Rbs(3,15) = -6.674611228927427e-10 Zbc(3,15) = -7.839977605379211e-10 + Rbc(4,15) = -1.309430166739691e-09 Zbs(4,15) = 1.307323987449241e-09 Rbs(4,15) = -1.112799418250951e-09 Zbc(4,15) = -1.332495657472441e-09 + Rbc(5,15) = 4.840003928332541e-10 Zbs(5,15) = -5.832474317460519e-11 Rbs(5,15) = 3.307668760801972e-09 Zbc(5,15) = 3.068617207357393e-09 + Rbc(6,15) = -5.814632699249499e-10 Zbs(6,15) = 3.134034416277007e-10 Rbs(6,15) = -2.593826584575602e-09 Zbc(6,15) = -1.767749803787374e-09 + Rbc(7,15) = -4.237469762239917e-10 Zbs(7,15) = 6.656534280439514e-10 Rbs(7,15) = 9.188969476656588e-10 Zbc(7,15) = 3.726041129866600e-10 + Rbc(8,15) = 2.006667605814383e-10 Zbs(8,15) = -8.530259533550436e-11 Rbs(8,15) = -7.646178744738401e-11 Zbc(8,15) = 1.433833181801917e-10 + Rbc(-8,16) = 1.452067567244547e-13 Zbs(-8,16) = 1.424660806662282e-13 Rbs(-8,16) = -3.748731292425618e-14 Zbc(-8,16) = -6.864068476359277e-14 + Rbc(-7,16) = 2.140174405762726e-13 Zbs(-7,16) = 1.880300085092135e-14 Rbs(-7,16) = -3.816607176717292e-14 Zbc(-7,16) = -1.082046022099694e-14 + Rbc(-6,16) = 1.176160707461745e-14 Zbs(-6,16) = -8.567190909901492e-14 Rbs(-6,16) = -1.483013428337749e-13 Zbc(-6,16) = -1.819515011375333e-14 + Rbc(-5,16) = -1.279086736089932e-13 Zbs(-5,16) = 4.976482266988766e-14 Rbs(-5,16) = 3.434132513109730e-15 Zbc(-5,16) = -7.140265658935349e-14 + Rbc(-4,16) = 2.888865772979339e-14 Zbs(-4,16) = 4.531636170947510e-14 Rbs(-4,16) = 1.007702405824155e-13 Zbc(-4,16) = 7.295026318712968e-14 + Rbc(-3,16) = 9.299183407920547e-14 Zbs(-3,16) = -8.911353067446020e-14 Rbs(-3,16) = -3.692491235113382e-14 Zbc(-3,16) = -2.562223302659898e-14 + Rbc(-2,16) = 1.970623477911608e-14 Zbs(-2,16) = 3.053986160536689e-14 Rbs(-2,16) = -5.502771662310535e-13 Zbc(-2,16) = -5.649979908037159e-13 + Rbc(-1,16) = -3.436544445286912e-12 Zbs(-1,16) = 3.397874617944665e-12 Rbs(-1,16) = -2.894899857984549e-12 Zbc(-1,16) = -2.673581446559611e-12 + Rbc(0,16) = -2.015780117437911e-11 Zbs(0,16) = 1.904903839309727e-11 Rbs(0,16) = 4.558496259068993e-12 Zbc(0,16) = 4.943412905364193e-12 + Rbc(1,16) = -2.260473750104799e-11 Zbs(1,16) = 1.802775047045661e-11 Rbs(1,16) = 3.154761712203857e-11 Zbc(1,16) = 2.854485230975698e-11 + Rbc(2,16) = 3.146023602473277e-11 Zbs(2,16) = -3.200334249794734e-11 Rbs(2,16) = 1.235523589709326e-10 Zbc(2,16) = 1.083675793795432e-10 + Rbc(3,16) = 3.299679997941966e-10 Zbs(3,16) = -3.237201236055901e-10 Rbs(3,16) = -2.906342160040384e-11 Zbc(3,16) = -5.505522444442261e-11 + Rbc(4,16) = 1.006581373944903e-10 Zbs(4,16) = -5.098591061840549e-11 Rbs(4,16) = -4.554781522369803e-10 Zbc(4,16) = -4.224619472094764e-10 + Rbc(5,16) = 3.638712291982905e-10 Zbs(5,16) = -5.454785559869727e-10 Rbs(5,16) = 2.335488795686364e-10 Zbc(5,16) = 2.760820514153359e-10 + Rbc(6,16) = -2.325504013083862e-10 Zbs(6,16) = 5.299897508497867e-10 Rbs(6,16) = -7.681751638979127e-10 Zbc(6,16) = -1.097741241100646e-09 + Rbc(7,16) = -2.186631160326478e-10 Zbs(7,16) = 4.275332945476761e-11 Rbs(7,16) = 4.552216241053028e-10 Zbc(7,16) = 1.060143854431083e-09 + Rbc(8,16) = 5.520247835663079e-11 Zbs(8,16) = -2.269020618449351e-10 Rbs(8,16) = 2.987973930439188e-11 Zbc(8,16) = -1.878641417807124e-10 + Rwc(0,0) = 3.000000000000000e+00 Zws(0,0) = 0.000000000000000e+00 Rws(0,0) = 0.000000000000000e+00 Zwc(0,0) = 0.000000000000000e+00 + Rwc(1,0) = 0.000000000000000e+00 Zws(1,0) = -6.000000000000000e-02 Rws(1,0) = 3.000000000000000e-02 Zwc(1,0) = 0.000000000000000e+00 + Rwc(2,0) = 0.000000000000000e+00 Zws(2,0) = 0.000000000000000e+00 Rws(2,0) = 0.000000000000000e+00 Zwc(2,0) = 0.000000000000000e+00 + Rwc(3,0) = 0.000000000000000e+00 Zws(3,0) = 0.000000000000000e+00 Rws(3,0) = 0.000000000000000e+00 Zwc(3,0) = 0.000000000000000e+00 + Rwc(4,0) = 0.000000000000000e+00 Zws(4,0) = 0.000000000000000e+00 Rws(4,0) = 0.000000000000000e+00 Zwc(4,0) = 0.000000000000000e+00 + Rwc(5,0) = 0.000000000000000e+00 Zws(5,0) = 0.000000000000000e+00 Rws(5,0) = 0.000000000000000e+00 Zwc(5,0) = 0.000000000000000e+00 + Rwc(6,0) = 0.000000000000000e+00 Zws(6,0) = 0.000000000000000e+00 Rws(6,0) = 0.000000000000000e+00 Zwc(6,0) = 0.000000000000000e+00 + Rwc(7,0) = 0.000000000000000e+00 Zws(7,0) = 0.000000000000000e+00 Rws(7,0) = 0.000000000000000e+00 Zwc(7,0) = 0.000000000000000e+00 + Rwc(8,0) = 0.000000000000000e+00 Zws(8,0) = 0.000000000000000e+00 Rws(8,0) = 0.000000000000000e+00 Zwc(8,0) = 0.000000000000000e+00 + Rwc(-8,1) = 0.000000000000000e+00 Zws(-8,1) = 0.000000000000000e+00 Rws(-8,1) = 0.000000000000000e+00 Zwc(-8,1) = 0.000000000000000e+00 + Rwc(-7,1) = 0.000000000000000e+00 Zws(-7,1) = 0.000000000000000e+00 Rws(-7,1) = 0.000000000000000e+00 Zwc(-7,1) = 0.000000000000000e+00 + Rwc(-6,1) = 0.000000000000000e+00 Zws(-6,1) = 0.000000000000000e+00 Rws(-6,1) = 0.000000000000000e+00 Zwc(-6,1) = 0.000000000000000e+00 + Rwc(-5,1) = 0.000000000000000e+00 Zws(-5,1) = 0.000000000000000e+00 Rws(-5,1) = 0.000000000000000e+00 Zwc(-5,1) = 0.000000000000000e+00 + Rwc(-4,1) = 0.000000000000000e+00 Zws(-4,1) = 0.000000000000000e+00 Rws(-4,1) = 0.000000000000000e+00 Zwc(-4,1) = 0.000000000000000e+00 + Rwc(-3,1) = 0.000000000000000e+00 Zws(-3,1) = 0.000000000000000e+00 Rws(-3,1) = 0.000000000000000e+00 Zwc(-3,1) = 0.000000000000000e+00 + Rwc(-2,1) = 0.000000000000000e+00 Zws(-2,1) = 0.000000000000000e+00 Rws(-2,1) = 0.000000000000000e+00 Zwc(-2,1) = 0.000000000000000e+00 + Rwc(-1,1) = 0.000000000000000e+00 Zws(-1,1) = 0.000000000000000e+00 Rws(-1,1) = 0.000000000000000e+00 Zwc(-1,1) = 0.000000000000000e+00 + Rwc(0,1) = 4.500000000000000e-01 Zws(0,1) = -4.500000000000000e-01 Rws(0,1) = 0.000000000000000e+00 Zwc(0,1) = 0.000000000000000e+00 + Rwc(1,1) = -6.000000000000000e-02 Zws(1,1) = -6.000000000000000e-02 Rws(1,1) = 3.000000000000000e-02 Zwc(1,1) = 0.000000000000000e+00 + Rwc(2,1) = 0.000000000000000e+00 Zws(2,1) = 0.000000000000000e+00 Rws(2,1) = 0.000000000000000e+00 Zwc(2,1) = 0.000000000000000e+00 + Rwc(3,1) = 0.000000000000000e+00 Zws(3,1) = 0.000000000000000e+00 Rws(3,1) = 0.000000000000000e+00 Zwc(3,1) = 0.000000000000000e+00 + Rwc(4,1) = 0.000000000000000e+00 Zws(4,1) = 0.000000000000000e+00 Rws(4,1) = 0.000000000000000e+00 Zwc(4,1) = 0.000000000000000e+00 + Rwc(5,1) = 0.000000000000000e+00 Zws(5,1) = 0.000000000000000e+00 Rws(5,1) = 0.000000000000000e+00 Zwc(5,1) = 0.000000000000000e+00 + Rwc(6,1) = 0.000000000000000e+00 Zws(6,1) = 0.000000000000000e+00 Rws(6,1) = 0.000000000000000e+00 Zwc(6,1) = 0.000000000000000e+00 + Rwc(7,1) = 0.000000000000000e+00 Zws(7,1) = 0.000000000000000e+00 Rws(7,1) = 0.000000000000000e+00 Zwc(7,1) = 0.000000000000000e+00 + Rwc(8,1) = 0.000000000000000e+00 Zws(8,1) = 0.000000000000000e+00 Rws(8,1) = 0.000000000000000e+00 Zwc(8,1) = 0.000000000000000e+00 + Rwc(-8,2) = 0.000000000000000e+00 Zws(-8,2) = 0.000000000000000e+00 Rws(-8,2) = 0.000000000000000e+00 Zwc(-8,2) = 0.000000000000000e+00 + Rwc(-7,2) = 0.000000000000000e+00 Zws(-7,2) = 0.000000000000000e+00 Rws(-7,2) = 0.000000000000000e+00 Zwc(-7,2) = 0.000000000000000e+00 + Rwc(-6,2) = 0.000000000000000e+00 Zws(-6,2) = 0.000000000000000e+00 Rws(-6,2) = 0.000000000000000e+00 Zwc(-6,2) = 0.000000000000000e+00 + Rwc(-5,2) = 0.000000000000000e+00 Zws(-5,2) = 0.000000000000000e+00 Rws(-5,2) = 0.000000000000000e+00 Zwc(-5,2) = 0.000000000000000e+00 + Rwc(-4,2) = 0.000000000000000e+00 Zws(-4,2) = 0.000000000000000e+00 Rws(-4,2) = 0.000000000000000e+00 Zwc(-4,2) = 0.000000000000000e+00 + Rwc(-3,2) = 0.000000000000000e+00 Zws(-3,2) = 0.000000000000000e+00 Rws(-3,2) = 0.000000000000000e+00 Zwc(-3,2) = 0.000000000000000e+00 + Rwc(-2,2) = 0.000000000000000e+00 Zws(-2,2) = 0.000000000000000e+00 Rws(-2,2) = 0.000000000000000e+00 Zwc(-2,2) = 0.000000000000000e+00 + Rwc(-1,2) = 0.000000000000000e+00 Zws(-1,2) = 0.000000000000000e+00 Rws(-1,2) = 0.000000000000000e+00 Zwc(-1,2) = 0.000000000000000e+00 + Rwc(0,2) = 0.000000000000000e+00 Zws(0,2) = 0.000000000000000e+00 Rws(0,2) = 0.000000000000000e+00 Zwc(0,2) = 0.000000000000000e+00 + Rwc(1,2) = 0.000000000000000e+00 Zws(1,2) = 0.000000000000000e+00 Rws(1,2) = 0.000000000000000e+00 Zwc(1,2) = 0.000000000000000e+00 + Rwc(2,2) = 0.000000000000000e+00 Zws(2,2) = 0.000000000000000e+00 Rws(2,2) = 0.000000000000000e+00 Zwc(2,2) = 0.000000000000000e+00 + Rwc(3,2) = 0.000000000000000e+00 Zws(3,2) = 0.000000000000000e+00 Rws(3,2) = 0.000000000000000e+00 Zwc(3,2) = 0.000000000000000e+00 + Rwc(4,2) = 0.000000000000000e+00 Zws(4,2) = 0.000000000000000e+00 Rws(4,2) = 0.000000000000000e+00 Zwc(4,2) = 0.000000000000000e+00 + Rwc(5,2) = 0.000000000000000e+00 Zws(5,2) = 0.000000000000000e+00 Rws(5,2) = 0.000000000000000e+00 Zwc(5,2) = 0.000000000000000e+00 + Rwc(6,2) = 0.000000000000000e+00 Zws(6,2) = 0.000000000000000e+00 Rws(6,2) = 0.000000000000000e+00 Zwc(6,2) = 0.000000000000000e+00 + Rwc(7,2) = 0.000000000000000e+00 Zws(7,2) = 0.000000000000000e+00 Rws(7,2) = 0.000000000000000e+00 Zwc(7,2) = 0.000000000000000e+00 + Rwc(8,2) = 0.000000000000000e+00 Zws(8,2) = 0.000000000000000e+00 Rws(8,2) = 0.000000000000000e+00 Zwc(8,2) = 0.000000000000000e+00 + Rwc(-8,3) = 0.000000000000000e+00 Zws(-8,3) = 0.000000000000000e+00 Rws(-8,3) = 0.000000000000000e+00 Zwc(-8,3) = 0.000000000000000e+00 + Rwc(-7,3) = 0.000000000000000e+00 Zws(-7,3) = 0.000000000000000e+00 Rws(-7,3) = 0.000000000000000e+00 Zwc(-7,3) = 0.000000000000000e+00 + Rwc(-6,3) = 0.000000000000000e+00 Zws(-6,3) = 0.000000000000000e+00 Rws(-6,3) = 0.000000000000000e+00 Zwc(-6,3) = 0.000000000000000e+00 + Rwc(-5,3) = 0.000000000000000e+00 Zws(-5,3) = 0.000000000000000e+00 Rws(-5,3) = 0.000000000000000e+00 Zwc(-5,3) = 0.000000000000000e+00 + Rwc(-4,3) = 0.000000000000000e+00 Zws(-4,3) = 0.000000000000000e+00 Rws(-4,3) = 0.000000000000000e+00 Zwc(-4,3) = 0.000000000000000e+00 + Rwc(-3,3) = 0.000000000000000e+00 Zws(-3,3) = 0.000000000000000e+00 Rws(-3,3) = 0.000000000000000e+00 Zwc(-3,3) = 0.000000000000000e+00 + Rwc(-2,3) = 0.000000000000000e+00 Zws(-2,3) = 0.000000000000000e+00 Rws(-2,3) = 0.000000000000000e+00 Zwc(-2,3) = 0.000000000000000e+00 + Rwc(-1,3) = 0.000000000000000e+00 Zws(-1,3) = 0.000000000000000e+00 Rws(-1,3) = 0.000000000000000e+00 Zwc(-1,3) = 0.000000000000000e+00 + Rwc(0,3) = 0.000000000000000e+00 Zws(0,3) = 0.000000000000000e+00 Rws(0,3) = 0.000000000000000e+00 Zwc(0,3) = 0.000000000000000e+00 + Rwc(1,3) = 0.000000000000000e+00 Zws(1,3) = 0.000000000000000e+00 Rws(1,3) = 0.000000000000000e+00 Zwc(1,3) = 0.000000000000000e+00 + Rwc(2,3) = 0.000000000000000e+00 Zws(2,3) = 0.000000000000000e+00 Rws(2,3) = 0.000000000000000e+00 Zwc(2,3) = 0.000000000000000e+00 + Rwc(3,3) = 0.000000000000000e+00 Zws(3,3) = 0.000000000000000e+00 Rws(3,3) = 0.000000000000000e+00 Zwc(3,3) = 0.000000000000000e+00 + Rwc(4,3) = 0.000000000000000e+00 Zws(4,3) = 0.000000000000000e+00 Rws(4,3) = 0.000000000000000e+00 Zwc(4,3) = 0.000000000000000e+00 + Rwc(5,3) = 0.000000000000000e+00 Zws(5,3) = 0.000000000000000e+00 Rws(5,3) = 0.000000000000000e+00 Zwc(5,3) = 0.000000000000000e+00 + Rwc(6,3) = 0.000000000000000e+00 Zws(6,3) = 0.000000000000000e+00 Rws(6,3) = 0.000000000000000e+00 Zwc(6,3) = 0.000000000000000e+00 + Rwc(7,3) = 0.000000000000000e+00 Zws(7,3) = 0.000000000000000e+00 Rws(7,3) = 0.000000000000000e+00 Zwc(7,3) = 0.000000000000000e+00 + Rwc(8,3) = 0.000000000000000e+00 Zws(8,3) = 0.000000000000000e+00 Rws(8,3) = 0.000000000000000e+00 Zwc(8,3) = 0.000000000000000e+00 + Rwc(-8,4) = 0.000000000000000e+00 Zws(-8,4) = 0.000000000000000e+00 Rws(-8,4) = 0.000000000000000e+00 Zwc(-8,4) = 0.000000000000000e+00 + Rwc(-7,4) = 0.000000000000000e+00 Zws(-7,4) = 0.000000000000000e+00 Rws(-7,4) = 0.000000000000000e+00 Zwc(-7,4) = 0.000000000000000e+00 + Rwc(-6,4) = 0.000000000000000e+00 Zws(-6,4) = 0.000000000000000e+00 Rws(-6,4) = 0.000000000000000e+00 Zwc(-6,4) = 0.000000000000000e+00 + Rwc(-5,4) = 0.000000000000000e+00 Zws(-5,4) = 0.000000000000000e+00 Rws(-5,4) = 0.000000000000000e+00 Zwc(-5,4) = 0.000000000000000e+00 + Rwc(-4,4) = 0.000000000000000e+00 Zws(-4,4) = 0.000000000000000e+00 Rws(-4,4) = 0.000000000000000e+00 Zwc(-4,4) = 0.000000000000000e+00 + Rwc(-3,4) = 0.000000000000000e+00 Zws(-3,4) = 0.000000000000000e+00 Rws(-3,4) = 0.000000000000000e+00 Zwc(-3,4) = 0.000000000000000e+00 + Rwc(-2,4) = 0.000000000000000e+00 Zws(-2,4) = 0.000000000000000e+00 Rws(-2,4) = 0.000000000000000e+00 Zwc(-2,4) = 0.000000000000000e+00 + Rwc(-1,4) = 0.000000000000000e+00 Zws(-1,4) = 0.000000000000000e+00 Rws(-1,4) = 0.000000000000000e+00 Zwc(-1,4) = 0.000000000000000e+00 + Rwc(0,4) = 0.000000000000000e+00 Zws(0,4) = 0.000000000000000e+00 Rws(0,4) = 0.000000000000000e+00 Zwc(0,4) = 0.000000000000000e+00 + Rwc(1,4) = 0.000000000000000e+00 Zws(1,4) = 0.000000000000000e+00 Rws(1,4) = 0.000000000000000e+00 Zwc(1,4) = 0.000000000000000e+00 + Rwc(2,4) = 0.000000000000000e+00 Zws(2,4) = 0.000000000000000e+00 Rws(2,4) = 0.000000000000000e+00 Zwc(2,4) = 0.000000000000000e+00 + Rwc(3,4) = 0.000000000000000e+00 Zws(3,4) = 0.000000000000000e+00 Rws(3,4) = 0.000000000000000e+00 Zwc(3,4) = 0.000000000000000e+00 + Rwc(4,4) = 0.000000000000000e+00 Zws(4,4) = 0.000000000000000e+00 Rws(4,4) = 0.000000000000000e+00 Zwc(4,4) = 0.000000000000000e+00 + Rwc(5,4) = 0.000000000000000e+00 Zws(5,4) = 0.000000000000000e+00 Rws(5,4) = 0.000000000000000e+00 Zwc(5,4) = 0.000000000000000e+00 + Rwc(6,4) = 0.000000000000000e+00 Zws(6,4) = 0.000000000000000e+00 Rws(6,4) = 0.000000000000000e+00 Zwc(6,4) = 0.000000000000000e+00 + Rwc(7,4) = 0.000000000000000e+00 Zws(7,4) = 0.000000000000000e+00 Rws(7,4) = 0.000000000000000e+00 Zwc(7,4) = 0.000000000000000e+00 + Rwc(8,4) = 0.000000000000000e+00 Zws(8,4) = 0.000000000000000e+00 Rws(8,4) = 0.000000000000000e+00 Zwc(8,4) = 0.000000000000000e+00 + Rwc(-8,5) = 0.000000000000000e+00 Zws(-8,5) = 0.000000000000000e+00 Rws(-8,5) = 0.000000000000000e+00 Zwc(-8,5) = 0.000000000000000e+00 + Rwc(-7,5) = 0.000000000000000e+00 Zws(-7,5) = 0.000000000000000e+00 Rws(-7,5) = 0.000000000000000e+00 Zwc(-7,5) = 0.000000000000000e+00 + Rwc(-6,5) = 0.000000000000000e+00 Zws(-6,5) = 0.000000000000000e+00 Rws(-6,5) = 0.000000000000000e+00 Zwc(-6,5) = 0.000000000000000e+00 + Rwc(-5,5) = 0.000000000000000e+00 Zws(-5,5) = 0.000000000000000e+00 Rws(-5,5) = 0.000000000000000e+00 Zwc(-5,5) = 0.000000000000000e+00 + Rwc(-4,5) = 0.000000000000000e+00 Zws(-4,5) = 0.000000000000000e+00 Rws(-4,5) = 0.000000000000000e+00 Zwc(-4,5) = 0.000000000000000e+00 + Rwc(-3,5) = 0.000000000000000e+00 Zws(-3,5) = 0.000000000000000e+00 Rws(-3,5) = 0.000000000000000e+00 Zwc(-3,5) = 0.000000000000000e+00 + Rwc(-2,5) = 0.000000000000000e+00 Zws(-2,5) = 0.000000000000000e+00 Rws(-2,5) = 0.000000000000000e+00 Zwc(-2,5) = 0.000000000000000e+00 + Rwc(-1,5) = 0.000000000000000e+00 Zws(-1,5) = 0.000000000000000e+00 Rws(-1,5) = 0.000000000000000e+00 Zwc(-1,5) = 0.000000000000000e+00 + Rwc(0,5) = 0.000000000000000e+00 Zws(0,5) = 0.000000000000000e+00 Rws(0,5) = 0.000000000000000e+00 Zwc(0,5) = 0.000000000000000e+00 + Rwc(1,5) = 0.000000000000000e+00 Zws(1,5) = 0.000000000000000e+00 Rws(1,5) = 0.000000000000000e+00 Zwc(1,5) = 0.000000000000000e+00 + Rwc(2,5) = 0.000000000000000e+00 Zws(2,5) = 0.000000000000000e+00 Rws(2,5) = 0.000000000000000e+00 Zwc(2,5) = 0.000000000000000e+00 + Rwc(3,5) = 0.000000000000000e+00 Zws(3,5) = 0.000000000000000e+00 Rws(3,5) = 0.000000000000000e+00 Zwc(3,5) = 0.000000000000000e+00 + Rwc(4,5) = 0.000000000000000e+00 Zws(4,5) = 0.000000000000000e+00 Rws(4,5) = 0.000000000000000e+00 Zwc(4,5) = 0.000000000000000e+00 + Rwc(5,5) = 0.000000000000000e+00 Zws(5,5) = 0.000000000000000e+00 Rws(5,5) = 0.000000000000000e+00 Zwc(5,5) = 0.000000000000000e+00 + Rwc(6,5) = 0.000000000000000e+00 Zws(6,5) = 0.000000000000000e+00 Rws(6,5) = 0.000000000000000e+00 Zwc(6,5) = 0.000000000000000e+00 + Rwc(7,5) = 0.000000000000000e+00 Zws(7,5) = 0.000000000000000e+00 Rws(7,5) = 0.000000000000000e+00 Zwc(7,5) = 0.000000000000000e+00 + Rwc(8,5) = 0.000000000000000e+00 Zws(8,5) = 0.000000000000000e+00 Rws(8,5) = 0.000000000000000e+00 Zwc(8,5) = 0.000000000000000e+00 + Rwc(-8,6) = 0.000000000000000e+00 Zws(-8,6) = 0.000000000000000e+00 Rws(-8,6) = 0.000000000000000e+00 Zwc(-8,6) = 0.000000000000000e+00 + Rwc(-7,6) = 0.000000000000000e+00 Zws(-7,6) = 0.000000000000000e+00 Rws(-7,6) = 0.000000000000000e+00 Zwc(-7,6) = 0.000000000000000e+00 + Rwc(-6,6) = 0.000000000000000e+00 Zws(-6,6) = 0.000000000000000e+00 Rws(-6,6) = 0.000000000000000e+00 Zwc(-6,6) = 0.000000000000000e+00 + Rwc(-5,6) = 0.000000000000000e+00 Zws(-5,6) = 0.000000000000000e+00 Rws(-5,6) = 0.000000000000000e+00 Zwc(-5,6) = 0.000000000000000e+00 + Rwc(-4,6) = 0.000000000000000e+00 Zws(-4,6) = 0.000000000000000e+00 Rws(-4,6) = 0.000000000000000e+00 Zwc(-4,6) = 0.000000000000000e+00 + Rwc(-3,6) = 0.000000000000000e+00 Zws(-3,6) = 0.000000000000000e+00 Rws(-3,6) = 0.000000000000000e+00 Zwc(-3,6) = 0.000000000000000e+00 + Rwc(-2,6) = 0.000000000000000e+00 Zws(-2,6) = 0.000000000000000e+00 Rws(-2,6) = 0.000000000000000e+00 Zwc(-2,6) = 0.000000000000000e+00 + Rwc(-1,6) = 0.000000000000000e+00 Zws(-1,6) = 0.000000000000000e+00 Rws(-1,6) = 0.000000000000000e+00 Zwc(-1,6) = 0.000000000000000e+00 + Rwc(0,6) = 0.000000000000000e+00 Zws(0,6) = 0.000000000000000e+00 Rws(0,6) = 0.000000000000000e+00 Zwc(0,6) = 0.000000000000000e+00 + Rwc(1,6) = 0.000000000000000e+00 Zws(1,6) = 0.000000000000000e+00 Rws(1,6) = 0.000000000000000e+00 Zwc(1,6) = 0.000000000000000e+00 + Rwc(2,6) = 0.000000000000000e+00 Zws(2,6) = 0.000000000000000e+00 Rws(2,6) = 0.000000000000000e+00 Zwc(2,6) = 0.000000000000000e+00 + Rwc(3,6) = 0.000000000000000e+00 Zws(3,6) = 0.000000000000000e+00 Rws(3,6) = 0.000000000000000e+00 Zwc(3,6) = 0.000000000000000e+00 + Rwc(4,6) = 0.000000000000000e+00 Zws(4,6) = 0.000000000000000e+00 Rws(4,6) = 0.000000000000000e+00 Zwc(4,6) = 0.000000000000000e+00 + Rwc(5,6) = 0.000000000000000e+00 Zws(5,6) = 0.000000000000000e+00 Rws(5,6) = 0.000000000000000e+00 Zwc(5,6) = 0.000000000000000e+00 + Rwc(6,6) = 0.000000000000000e+00 Zws(6,6) = 0.000000000000000e+00 Rws(6,6) = 0.000000000000000e+00 Zwc(6,6) = 0.000000000000000e+00 + Rwc(7,6) = 0.000000000000000e+00 Zws(7,6) = 0.000000000000000e+00 Rws(7,6) = 0.000000000000000e+00 Zwc(7,6) = 0.000000000000000e+00 + Rwc(8,6) = 0.000000000000000e+00 Zws(8,6) = 0.000000000000000e+00 Rws(8,6) = 0.000000000000000e+00 Zwc(8,6) = 0.000000000000000e+00 + Rwc(-8,7) = 0.000000000000000e+00 Zws(-8,7) = 0.000000000000000e+00 Rws(-8,7) = 0.000000000000000e+00 Zwc(-8,7) = 0.000000000000000e+00 + Rwc(-7,7) = 0.000000000000000e+00 Zws(-7,7) = 0.000000000000000e+00 Rws(-7,7) = 0.000000000000000e+00 Zwc(-7,7) = 0.000000000000000e+00 + Rwc(-6,7) = 0.000000000000000e+00 Zws(-6,7) = 0.000000000000000e+00 Rws(-6,7) = 0.000000000000000e+00 Zwc(-6,7) = 0.000000000000000e+00 + Rwc(-5,7) = 0.000000000000000e+00 Zws(-5,7) = 0.000000000000000e+00 Rws(-5,7) = 0.000000000000000e+00 Zwc(-5,7) = 0.000000000000000e+00 + Rwc(-4,7) = 0.000000000000000e+00 Zws(-4,7) = 0.000000000000000e+00 Rws(-4,7) = 0.000000000000000e+00 Zwc(-4,7) = 0.000000000000000e+00 + Rwc(-3,7) = 0.000000000000000e+00 Zws(-3,7) = 0.000000000000000e+00 Rws(-3,7) = 0.000000000000000e+00 Zwc(-3,7) = 0.000000000000000e+00 + Rwc(-2,7) = 0.000000000000000e+00 Zws(-2,7) = 0.000000000000000e+00 Rws(-2,7) = 0.000000000000000e+00 Zwc(-2,7) = 0.000000000000000e+00 + Rwc(-1,7) = 0.000000000000000e+00 Zws(-1,7) = 0.000000000000000e+00 Rws(-1,7) = 0.000000000000000e+00 Zwc(-1,7) = 0.000000000000000e+00 + Rwc(0,7) = 0.000000000000000e+00 Zws(0,7) = 0.000000000000000e+00 Rws(0,7) = 0.000000000000000e+00 Zwc(0,7) = 0.000000000000000e+00 + Rwc(1,7) = 0.000000000000000e+00 Zws(1,7) = 0.000000000000000e+00 Rws(1,7) = 0.000000000000000e+00 Zwc(1,7) = 0.000000000000000e+00 + Rwc(2,7) = 0.000000000000000e+00 Zws(2,7) = 0.000000000000000e+00 Rws(2,7) = 0.000000000000000e+00 Zwc(2,7) = 0.000000000000000e+00 + Rwc(3,7) = 0.000000000000000e+00 Zws(3,7) = 0.000000000000000e+00 Rws(3,7) = 0.000000000000000e+00 Zwc(3,7) = 0.000000000000000e+00 + Rwc(4,7) = 0.000000000000000e+00 Zws(4,7) = 0.000000000000000e+00 Rws(4,7) = 0.000000000000000e+00 Zwc(4,7) = 0.000000000000000e+00 + Rwc(5,7) = 0.000000000000000e+00 Zws(5,7) = 0.000000000000000e+00 Rws(5,7) = 0.000000000000000e+00 Zwc(5,7) = 0.000000000000000e+00 + Rwc(6,7) = 0.000000000000000e+00 Zws(6,7) = 0.000000000000000e+00 Rws(6,7) = 0.000000000000000e+00 Zwc(6,7) = 0.000000000000000e+00 + Rwc(7,7) = 0.000000000000000e+00 Zws(7,7) = 0.000000000000000e+00 Rws(7,7) = 0.000000000000000e+00 Zwc(7,7) = 0.000000000000000e+00 + Rwc(8,7) = 0.000000000000000e+00 Zws(8,7) = 0.000000000000000e+00 Rws(8,7) = 0.000000000000000e+00 Zwc(8,7) = 0.000000000000000e+00 + Rwc(-8,8) = 0.000000000000000e+00 Zws(-8,8) = 0.000000000000000e+00 Rws(-8,8) = 0.000000000000000e+00 Zwc(-8,8) = 0.000000000000000e+00 + Rwc(-7,8) = 0.000000000000000e+00 Zws(-7,8) = 0.000000000000000e+00 Rws(-7,8) = 0.000000000000000e+00 Zwc(-7,8) = 0.000000000000000e+00 + Rwc(-6,8) = 0.000000000000000e+00 Zws(-6,8) = 0.000000000000000e+00 Rws(-6,8) = 0.000000000000000e+00 Zwc(-6,8) = 0.000000000000000e+00 + Rwc(-5,8) = 0.000000000000000e+00 Zws(-5,8) = 0.000000000000000e+00 Rws(-5,8) = 0.000000000000000e+00 Zwc(-5,8) = 0.000000000000000e+00 + Rwc(-4,8) = 0.000000000000000e+00 Zws(-4,8) = 0.000000000000000e+00 Rws(-4,8) = 0.000000000000000e+00 Zwc(-4,8) = 0.000000000000000e+00 + Rwc(-3,8) = 0.000000000000000e+00 Zws(-3,8) = 0.000000000000000e+00 Rws(-3,8) = 0.000000000000000e+00 Zwc(-3,8) = 0.000000000000000e+00 + Rwc(-2,8) = 0.000000000000000e+00 Zws(-2,8) = 0.000000000000000e+00 Rws(-2,8) = 0.000000000000000e+00 Zwc(-2,8) = 0.000000000000000e+00 + Rwc(-1,8) = 0.000000000000000e+00 Zws(-1,8) = 0.000000000000000e+00 Rws(-1,8) = 0.000000000000000e+00 Zwc(-1,8) = 0.000000000000000e+00 + Rwc(0,8) = 0.000000000000000e+00 Zws(0,8) = 0.000000000000000e+00 Rws(0,8) = 0.000000000000000e+00 Zwc(0,8) = 0.000000000000000e+00 + Rwc(1,8) = 0.000000000000000e+00 Zws(1,8) = 0.000000000000000e+00 Rws(1,8) = 0.000000000000000e+00 Zwc(1,8) = 0.000000000000000e+00 + Rwc(2,8) = 0.000000000000000e+00 Zws(2,8) = 0.000000000000000e+00 Rws(2,8) = 0.000000000000000e+00 Zwc(2,8) = 0.000000000000000e+00 + Rwc(3,8) = 0.000000000000000e+00 Zws(3,8) = 0.000000000000000e+00 Rws(3,8) = 0.000000000000000e+00 Zwc(3,8) = 0.000000000000000e+00 + Rwc(4,8) = 0.000000000000000e+00 Zws(4,8) = 0.000000000000000e+00 Rws(4,8) = 0.000000000000000e+00 Zwc(4,8) = 0.000000000000000e+00 + Rwc(5,8) = 0.000000000000000e+00 Zws(5,8) = 0.000000000000000e+00 Rws(5,8) = 0.000000000000000e+00 Zwc(5,8) = 0.000000000000000e+00 + Rwc(6,8) = 0.000000000000000e+00 Zws(6,8) = 0.000000000000000e+00 Rws(6,8) = 0.000000000000000e+00 Zwc(6,8) = 0.000000000000000e+00 + Rwc(7,8) = 0.000000000000000e+00 Zws(7,8) = 0.000000000000000e+00 Rws(7,8) = 0.000000000000000e+00 Zwc(7,8) = 0.000000000000000e+00 + Rwc(8,8) = 0.000000000000000e+00 Zws(8,8) = 0.000000000000000e+00 Rws(8,8) = 0.000000000000000e+00 Zwc(8,8) = 0.000000000000000e+00 + Rwc(-8,9) = 0.000000000000000e+00 Zws(-8,9) = 0.000000000000000e+00 Rws(-8,9) = 0.000000000000000e+00 Zwc(-8,9) = 0.000000000000000e+00 + Rwc(-7,9) = 0.000000000000000e+00 Zws(-7,9) = 0.000000000000000e+00 Rws(-7,9) = 0.000000000000000e+00 Zwc(-7,9) = 0.000000000000000e+00 + Rwc(-6,9) = 0.000000000000000e+00 Zws(-6,9) = 0.000000000000000e+00 Rws(-6,9) = 0.000000000000000e+00 Zwc(-6,9) = 0.000000000000000e+00 + Rwc(-5,9) = 0.000000000000000e+00 Zws(-5,9) = 0.000000000000000e+00 Rws(-5,9) = 0.000000000000000e+00 Zwc(-5,9) = 0.000000000000000e+00 + Rwc(-4,9) = 0.000000000000000e+00 Zws(-4,9) = 0.000000000000000e+00 Rws(-4,9) = 0.000000000000000e+00 Zwc(-4,9) = 0.000000000000000e+00 + Rwc(-3,9) = 0.000000000000000e+00 Zws(-3,9) = 0.000000000000000e+00 Rws(-3,9) = 0.000000000000000e+00 Zwc(-3,9) = 0.000000000000000e+00 + Rwc(-2,9) = 0.000000000000000e+00 Zws(-2,9) = 0.000000000000000e+00 Rws(-2,9) = 0.000000000000000e+00 Zwc(-2,9) = 0.000000000000000e+00 + Rwc(-1,9) = 0.000000000000000e+00 Zws(-1,9) = 0.000000000000000e+00 Rws(-1,9) = 0.000000000000000e+00 Zwc(-1,9) = 0.000000000000000e+00 + Rwc(0,9) = 0.000000000000000e+00 Zws(0,9) = 0.000000000000000e+00 Rws(0,9) = 0.000000000000000e+00 Zwc(0,9) = 0.000000000000000e+00 + Rwc(1,9) = 0.000000000000000e+00 Zws(1,9) = 0.000000000000000e+00 Rws(1,9) = 0.000000000000000e+00 Zwc(1,9) = 0.000000000000000e+00 + Rwc(2,9) = 0.000000000000000e+00 Zws(2,9) = 0.000000000000000e+00 Rws(2,9) = 0.000000000000000e+00 Zwc(2,9) = 0.000000000000000e+00 + Rwc(3,9) = 0.000000000000000e+00 Zws(3,9) = 0.000000000000000e+00 Rws(3,9) = 0.000000000000000e+00 Zwc(3,9) = 0.000000000000000e+00 + Rwc(4,9) = 0.000000000000000e+00 Zws(4,9) = 0.000000000000000e+00 Rws(4,9) = 0.000000000000000e+00 Zwc(4,9) = 0.000000000000000e+00 + Rwc(5,9) = 0.000000000000000e+00 Zws(5,9) = 0.000000000000000e+00 Rws(5,9) = 0.000000000000000e+00 Zwc(5,9) = 0.000000000000000e+00 + Rwc(6,9) = 0.000000000000000e+00 Zws(6,9) = 0.000000000000000e+00 Rws(6,9) = 0.000000000000000e+00 Zwc(6,9) = 0.000000000000000e+00 + Rwc(7,9) = 0.000000000000000e+00 Zws(7,9) = 0.000000000000000e+00 Rws(7,9) = 0.000000000000000e+00 Zwc(7,9) = 0.000000000000000e+00 + Rwc(8,9) = 0.000000000000000e+00 Zws(8,9) = 0.000000000000000e+00 Rws(8,9) = 0.000000000000000e+00 Zwc(8,9) = 0.000000000000000e+00 + Rwc(-8,10) = 0.000000000000000e+00 Zws(-8,10) = 0.000000000000000e+00 Rws(-8,10) = 0.000000000000000e+00 Zwc(-8,10) = 0.000000000000000e+00 + Rwc(-7,10) = 0.000000000000000e+00 Zws(-7,10) = 0.000000000000000e+00 Rws(-7,10) = 0.000000000000000e+00 Zwc(-7,10) = 0.000000000000000e+00 + Rwc(-6,10) = 0.000000000000000e+00 Zws(-6,10) = 0.000000000000000e+00 Rws(-6,10) = 0.000000000000000e+00 Zwc(-6,10) = 0.000000000000000e+00 + Rwc(-5,10) = 0.000000000000000e+00 Zws(-5,10) = 0.000000000000000e+00 Rws(-5,10) = 0.000000000000000e+00 Zwc(-5,10) = 0.000000000000000e+00 + Rwc(-4,10) = 0.000000000000000e+00 Zws(-4,10) = 0.000000000000000e+00 Rws(-4,10) = 0.000000000000000e+00 Zwc(-4,10) = 0.000000000000000e+00 + Rwc(-3,10) = 0.000000000000000e+00 Zws(-3,10) = 0.000000000000000e+00 Rws(-3,10) = 0.000000000000000e+00 Zwc(-3,10) = 0.000000000000000e+00 + Rwc(-2,10) = 0.000000000000000e+00 Zws(-2,10) = 0.000000000000000e+00 Rws(-2,10) = 0.000000000000000e+00 Zwc(-2,10) = 0.000000000000000e+00 + Rwc(-1,10) = 0.000000000000000e+00 Zws(-1,10) = 0.000000000000000e+00 Rws(-1,10) = 0.000000000000000e+00 Zwc(-1,10) = 0.000000000000000e+00 + Rwc(0,10) = 0.000000000000000e+00 Zws(0,10) = 0.000000000000000e+00 Rws(0,10) = 0.000000000000000e+00 Zwc(0,10) = 0.000000000000000e+00 + Rwc(1,10) = 0.000000000000000e+00 Zws(1,10) = 0.000000000000000e+00 Rws(1,10) = 0.000000000000000e+00 Zwc(1,10) = 0.000000000000000e+00 + Rwc(2,10) = 0.000000000000000e+00 Zws(2,10) = 0.000000000000000e+00 Rws(2,10) = 0.000000000000000e+00 Zwc(2,10) = 0.000000000000000e+00 + Rwc(3,10) = 0.000000000000000e+00 Zws(3,10) = 0.000000000000000e+00 Rws(3,10) = 0.000000000000000e+00 Zwc(3,10) = 0.000000000000000e+00 + Rwc(4,10) = 0.000000000000000e+00 Zws(4,10) = 0.000000000000000e+00 Rws(4,10) = 0.000000000000000e+00 Zwc(4,10) = 0.000000000000000e+00 + Rwc(5,10) = 0.000000000000000e+00 Zws(5,10) = 0.000000000000000e+00 Rws(5,10) = 0.000000000000000e+00 Zwc(5,10) = 0.000000000000000e+00 + Rwc(6,10) = 0.000000000000000e+00 Zws(6,10) = 0.000000000000000e+00 Rws(6,10) = 0.000000000000000e+00 Zwc(6,10) = 0.000000000000000e+00 + Rwc(7,10) = 0.000000000000000e+00 Zws(7,10) = 0.000000000000000e+00 Rws(7,10) = 0.000000000000000e+00 Zwc(7,10) = 0.000000000000000e+00 + Rwc(8,10) = 0.000000000000000e+00 Zws(8,10) = 0.000000000000000e+00 Rws(8,10) = 0.000000000000000e+00 Zwc(8,10) = 0.000000000000000e+00 + Rwc(-8,11) = 0.000000000000000e+00 Zws(-8,11) = 0.000000000000000e+00 Rws(-8,11) = 0.000000000000000e+00 Zwc(-8,11) = 0.000000000000000e+00 + Rwc(-7,11) = 0.000000000000000e+00 Zws(-7,11) = 0.000000000000000e+00 Rws(-7,11) = 0.000000000000000e+00 Zwc(-7,11) = 0.000000000000000e+00 + Rwc(-6,11) = 0.000000000000000e+00 Zws(-6,11) = 0.000000000000000e+00 Rws(-6,11) = 0.000000000000000e+00 Zwc(-6,11) = 0.000000000000000e+00 + Rwc(-5,11) = 0.000000000000000e+00 Zws(-5,11) = 0.000000000000000e+00 Rws(-5,11) = 0.000000000000000e+00 Zwc(-5,11) = 0.000000000000000e+00 + Rwc(-4,11) = 0.000000000000000e+00 Zws(-4,11) = 0.000000000000000e+00 Rws(-4,11) = 0.000000000000000e+00 Zwc(-4,11) = 0.000000000000000e+00 + Rwc(-3,11) = 0.000000000000000e+00 Zws(-3,11) = 0.000000000000000e+00 Rws(-3,11) = 0.000000000000000e+00 Zwc(-3,11) = 0.000000000000000e+00 + Rwc(-2,11) = 0.000000000000000e+00 Zws(-2,11) = 0.000000000000000e+00 Rws(-2,11) = 0.000000000000000e+00 Zwc(-2,11) = 0.000000000000000e+00 + Rwc(-1,11) = 0.000000000000000e+00 Zws(-1,11) = 0.000000000000000e+00 Rws(-1,11) = 0.000000000000000e+00 Zwc(-1,11) = 0.000000000000000e+00 + Rwc(0,11) = 0.000000000000000e+00 Zws(0,11) = 0.000000000000000e+00 Rws(0,11) = 0.000000000000000e+00 Zwc(0,11) = 0.000000000000000e+00 + Rwc(1,11) = 0.000000000000000e+00 Zws(1,11) = 0.000000000000000e+00 Rws(1,11) = 0.000000000000000e+00 Zwc(1,11) = 0.000000000000000e+00 + Rwc(2,11) = 0.000000000000000e+00 Zws(2,11) = 0.000000000000000e+00 Rws(2,11) = 0.000000000000000e+00 Zwc(2,11) = 0.000000000000000e+00 + Rwc(3,11) = 0.000000000000000e+00 Zws(3,11) = 0.000000000000000e+00 Rws(3,11) = 0.000000000000000e+00 Zwc(3,11) = 0.000000000000000e+00 + Rwc(4,11) = 0.000000000000000e+00 Zws(4,11) = 0.000000000000000e+00 Rws(4,11) = 0.000000000000000e+00 Zwc(4,11) = 0.000000000000000e+00 + Rwc(5,11) = 0.000000000000000e+00 Zws(5,11) = 0.000000000000000e+00 Rws(5,11) = 0.000000000000000e+00 Zwc(5,11) = 0.000000000000000e+00 + Rwc(6,11) = 0.000000000000000e+00 Zws(6,11) = 0.000000000000000e+00 Rws(6,11) = 0.000000000000000e+00 Zwc(6,11) = 0.000000000000000e+00 + Rwc(7,11) = 0.000000000000000e+00 Zws(7,11) = 0.000000000000000e+00 Rws(7,11) = 0.000000000000000e+00 Zwc(7,11) = 0.000000000000000e+00 + Rwc(8,11) = 0.000000000000000e+00 Zws(8,11) = 0.000000000000000e+00 Rws(8,11) = 0.000000000000000e+00 Zwc(8,11) = 0.000000000000000e+00 + Rwc(-8,12) = 0.000000000000000e+00 Zws(-8,12) = 0.000000000000000e+00 Rws(-8,12) = 0.000000000000000e+00 Zwc(-8,12) = 0.000000000000000e+00 + Rwc(-7,12) = 0.000000000000000e+00 Zws(-7,12) = 0.000000000000000e+00 Rws(-7,12) = 0.000000000000000e+00 Zwc(-7,12) = 0.000000000000000e+00 + Rwc(-6,12) = 0.000000000000000e+00 Zws(-6,12) = 0.000000000000000e+00 Rws(-6,12) = 0.000000000000000e+00 Zwc(-6,12) = 0.000000000000000e+00 + Rwc(-5,12) = 0.000000000000000e+00 Zws(-5,12) = 0.000000000000000e+00 Rws(-5,12) = 0.000000000000000e+00 Zwc(-5,12) = 0.000000000000000e+00 + Rwc(-4,12) = 0.000000000000000e+00 Zws(-4,12) = 0.000000000000000e+00 Rws(-4,12) = 0.000000000000000e+00 Zwc(-4,12) = 0.000000000000000e+00 + Rwc(-3,12) = 0.000000000000000e+00 Zws(-3,12) = 0.000000000000000e+00 Rws(-3,12) = 0.000000000000000e+00 Zwc(-3,12) = 0.000000000000000e+00 + Rwc(-2,12) = 0.000000000000000e+00 Zws(-2,12) = 0.000000000000000e+00 Rws(-2,12) = 0.000000000000000e+00 Zwc(-2,12) = 0.000000000000000e+00 + Rwc(-1,12) = 0.000000000000000e+00 Zws(-1,12) = 0.000000000000000e+00 Rws(-1,12) = 0.000000000000000e+00 Zwc(-1,12) = 0.000000000000000e+00 + Rwc(0,12) = 0.000000000000000e+00 Zws(0,12) = 0.000000000000000e+00 Rws(0,12) = 0.000000000000000e+00 Zwc(0,12) = 0.000000000000000e+00 + Rwc(1,12) = 0.000000000000000e+00 Zws(1,12) = 0.000000000000000e+00 Rws(1,12) = 0.000000000000000e+00 Zwc(1,12) = 0.000000000000000e+00 + Rwc(2,12) = 0.000000000000000e+00 Zws(2,12) = 0.000000000000000e+00 Rws(2,12) = 0.000000000000000e+00 Zwc(2,12) = 0.000000000000000e+00 + Rwc(3,12) = 0.000000000000000e+00 Zws(3,12) = 0.000000000000000e+00 Rws(3,12) = 0.000000000000000e+00 Zwc(3,12) = 0.000000000000000e+00 + Rwc(4,12) = 0.000000000000000e+00 Zws(4,12) = 0.000000000000000e+00 Rws(4,12) = 0.000000000000000e+00 Zwc(4,12) = 0.000000000000000e+00 + Rwc(5,12) = 0.000000000000000e+00 Zws(5,12) = 0.000000000000000e+00 Rws(5,12) = 0.000000000000000e+00 Zwc(5,12) = 0.000000000000000e+00 + Rwc(6,12) = 0.000000000000000e+00 Zws(6,12) = 0.000000000000000e+00 Rws(6,12) = 0.000000000000000e+00 Zwc(6,12) = 0.000000000000000e+00 + Rwc(7,12) = 0.000000000000000e+00 Zws(7,12) = 0.000000000000000e+00 Rws(7,12) = 0.000000000000000e+00 Zwc(7,12) = 0.000000000000000e+00 + Rwc(8,12) = 0.000000000000000e+00 Zws(8,12) = 0.000000000000000e+00 Rws(8,12) = 0.000000000000000e+00 Zwc(8,12) = 0.000000000000000e+00 + Rwc(-8,13) = 0.000000000000000e+00 Zws(-8,13) = 0.000000000000000e+00 Rws(-8,13) = 0.000000000000000e+00 Zwc(-8,13) = 0.000000000000000e+00 + Rwc(-7,13) = 0.000000000000000e+00 Zws(-7,13) = 0.000000000000000e+00 Rws(-7,13) = 0.000000000000000e+00 Zwc(-7,13) = 0.000000000000000e+00 + Rwc(-6,13) = 0.000000000000000e+00 Zws(-6,13) = 0.000000000000000e+00 Rws(-6,13) = 0.000000000000000e+00 Zwc(-6,13) = 0.000000000000000e+00 + Rwc(-5,13) = 0.000000000000000e+00 Zws(-5,13) = 0.000000000000000e+00 Rws(-5,13) = 0.000000000000000e+00 Zwc(-5,13) = 0.000000000000000e+00 + Rwc(-4,13) = 0.000000000000000e+00 Zws(-4,13) = 0.000000000000000e+00 Rws(-4,13) = 0.000000000000000e+00 Zwc(-4,13) = 0.000000000000000e+00 + Rwc(-3,13) = 0.000000000000000e+00 Zws(-3,13) = 0.000000000000000e+00 Rws(-3,13) = 0.000000000000000e+00 Zwc(-3,13) = 0.000000000000000e+00 + Rwc(-2,13) = 0.000000000000000e+00 Zws(-2,13) = 0.000000000000000e+00 Rws(-2,13) = 0.000000000000000e+00 Zwc(-2,13) = 0.000000000000000e+00 + Rwc(-1,13) = 0.000000000000000e+00 Zws(-1,13) = 0.000000000000000e+00 Rws(-1,13) = 0.000000000000000e+00 Zwc(-1,13) = 0.000000000000000e+00 + Rwc(0,13) = 0.000000000000000e+00 Zws(0,13) = 0.000000000000000e+00 Rws(0,13) = 0.000000000000000e+00 Zwc(0,13) = 0.000000000000000e+00 + Rwc(1,13) = 0.000000000000000e+00 Zws(1,13) = 0.000000000000000e+00 Rws(1,13) = 0.000000000000000e+00 Zwc(1,13) = 0.000000000000000e+00 + Rwc(2,13) = 0.000000000000000e+00 Zws(2,13) = 0.000000000000000e+00 Rws(2,13) = 0.000000000000000e+00 Zwc(2,13) = 0.000000000000000e+00 + Rwc(3,13) = 0.000000000000000e+00 Zws(3,13) = 0.000000000000000e+00 Rws(3,13) = 0.000000000000000e+00 Zwc(3,13) = 0.000000000000000e+00 + Rwc(4,13) = 0.000000000000000e+00 Zws(4,13) = 0.000000000000000e+00 Rws(4,13) = 0.000000000000000e+00 Zwc(4,13) = 0.000000000000000e+00 + Rwc(5,13) = 0.000000000000000e+00 Zws(5,13) = 0.000000000000000e+00 Rws(5,13) = 0.000000000000000e+00 Zwc(5,13) = 0.000000000000000e+00 + Rwc(6,13) = 0.000000000000000e+00 Zws(6,13) = 0.000000000000000e+00 Rws(6,13) = 0.000000000000000e+00 Zwc(6,13) = 0.000000000000000e+00 + Rwc(7,13) = 0.000000000000000e+00 Zws(7,13) = 0.000000000000000e+00 Rws(7,13) = 0.000000000000000e+00 Zwc(7,13) = 0.000000000000000e+00 + Rwc(8,13) = 0.000000000000000e+00 Zws(8,13) = 0.000000000000000e+00 Rws(8,13) = 0.000000000000000e+00 Zwc(8,13) = 0.000000000000000e+00 + Rwc(-8,14) = 0.000000000000000e+00 Zws(-8,14) = 0.000000000000000e+00 Rws(-8,14) = 0.000000000000000e+00 Zwc(-8,14) = 0.000000000000000e+00 + Rwc(-7,14) = 0.000000000000000e+00 Zws(-7,14) = 0.000000000000000e+00 Rws(-7,14) = 0.000000000000000e+00 Zwc(-7,14) = 0.000000000000000e+00 + Rwc(-6,14) = 0.000000000000000e+00 Zws(-6,14) = 0.000000000000000e+00 Rws(-6,14) = 0.000000000000000e+00 Zwc(-6,14) = 0.000000000000000e+00 + Rwc(-5,14) = 0.000000000000000e+00 Zws(-5,14) = 0.000000000000000e+00 Rws(-5,14) = 0.000000000000000e+00 Zwc(-5,14) = 0.000000000000000e+00 + Rwc(-4,14) = 0.000000000000000e+00 Zws(-4,14) = 0.000000000000000e+00 Rws(-4,14) = 0.000000000000000e+00 Zwc(-4,14) = 0.000000000000000e+00 + Rwc(-3,14) = 0.000000000000000e+00 Zws(-3,14) = 0.000000000000000e+00 Rws(-3,14) = 0.000000000000000e+00 Zwc(-3,14) = 0.000000000000000e+00 + Rwc(-2,14) = 0.000000000000000e+00 Zws(-2,14) = 0.000000000000000e+00 Rws(-2,14) = 0.000000000000000e+00 Zwc(-2,14) = 0.000000000000000e+00 + Rwc(-1,14) = 0.000000000000000e+00 Zws(-1,14) = 0.000000000000000e+00 Rws(-1,14) = 0.000000000000000e+00 Zwc(-1,14) = 0.000000000000000e+00 + Rwc(0,14) = 0.000000000000000e+00 Zws(0,14) = 0.000000000000000e+00 Rws(0,14) = 0.000000000000000e+00 Zwc(0,14) = 0.000000000000000e+00 + Rwc(1,14) = 0.000000000000000e+00 Zws(1,14) = 0.000000000000000e+00 Rws(1,14) = 0.000000000000000e+00 Zwc(1,14) = 0.000000000000000e+00 + Rwc(2,14) = 0.000000000000000e+00 Zws(2,14) = 0.000000000000000e+00 Rws(2,14) = 0.000000000000000e+00 Zwc(2,14) = 0.000000000000000e+00 + Rwc(3,14) = 0.000000000000000e+00 Zws(3,14) = 0.000000000000000e+00 Rws(3,14) = 0.000000000000000e+00 Zwc(3,14) = 0.000000000000000e+00 + Rwc(4,14) = 0.000000000000000e+00 Zws(4,14) = 0.000000000000000e+00 Rws(4,14) = 0.000000000000000e+00 Zwc(4,14) = 0.000000000000000e+00 + Rwc(5,14) = 0.000000000000000e+00 Zws(5,14) = 0.000000000000000e+00 Rws(5,14) = 0.000000000000000e+00 Zwc(5,14) = 0.000000000000000e+00 + Rwc(6,14) = 0.000000000000000e+00 Zws(6,14) = 0.000000000000000e+00 Rws(6,14) = 0.000000000000000e+00 Zwc(6,14) = 0.000000000000000e+00 + Rwc(7,14) = 0.000000000000000e+00 Zws(7,14) = 0.000000000000000e+00 Rws(7,14) = 0.000000000000000e+00 Zwc(7,14) = 0.000000000000000e+00 + Rwc(8,14) = 0.000000000000000e+00 Zws(8,14) = 0.000000000000000e+00 Rws(8,14) = 0.000000000000000e+00 Zwc(8,14) = 0.000000000000000e+00 + Rwc(-8,15) = 0.000000000000000e+00 Zws(-8,15) = 0.000000000000000e+00 Rws(-8,15) = 0.000000000000000e+00 Zwc(-8,15) = 0.000000000000000e+00 + Rwc(-7,15) = 0.000000000000000e+00 Zws(-7,15) = 0.000000000000000e+00 Rws(-7,15) = 0.000000000000000e+00 Zwc(-7,15) = 0.000000000000000e+00 + Rwc(-6,15) = 0.000000000000000e+00 Zws(-6,15) = 0.000000000000000e+00 Rws(-6,15) = 0.000000000000000e+00 Zwc(-6,15) = 0.000000000000000e+00 + Rwc(-5,15) = 0.000000000000000e+00 Zws(-5,15) = 0.000000000000000e+00 Rws(-5,15) = 0.000000000000000e+00 Zwc(-5,15) = 0.000000000000000e+00 + Rwc(-4,15) = 0.000000000000000e+00 Zws(-4,15) = 0.000000000000000e+00 Rws(-4,15) = 0.000000000000000e+00 Zwc(-4,15) = 0.000000000000000e+00 + Rwc(-3,15) = 0.000000000000000e+00 Zws(-3,15) = 0.000000000000000e+00 Rws(-3,15) = 0.000000000000000e+00 Zwc(-3,15) = 0.000000000000000e+00 + Rwc(-2,15) = 0.000000000000000e+00 Zws(-2,15) = 0.000000000000000e+00 Rws(-2,15) = 0.000000000000000e+00 Zwc(-2,15) = 0.000000000000000e+00 + Rwc(-1,15) = 0.000000000000000e+00 Zws(-1,15) = 0.000000000000000e+00 Rws(-1,15) = 0.000000000000000e+00 Zwc(-1,15) = 0.000000000000000e+00 + Rwc(0,15) = 0.000000000000000e+00 Zws(0,15) = 0.000000000000000e+00 Rws(0,15) = 0.000000000000000e+00 Zwc(0,15) = 0.000000000000000e+00 + Rwc(1,15) = 0.000000000000000e+00 Zws(1,15) = 0.000000000000000e+00 Rws(1,15) = 0.000000000000000e+00 Zwc(1,15) = 0.000000000000000e+00 + Rwc(2,15) = 0.000000000000000e+00 Zws(2,15) = 0.000000000000000e+00 Rws(2,15) = 0.000000000000000e+00 Zwc(2,15) = 0.000000000000000e+00 + Rwc(3,15) = 0.000000000000000e+00 Zws(3,15) = 0.000000000000000e+00 Rws(3,15) = 0.000000000000000e+00 Zwc(3,15) = 0.000000000000000e+00 + Rwc(4,15) = 0.000000000000000e+00 Zws(4,15) = 0.000000000000000e+00 Rws(4,15) = 0.000000000000000e+00 Zwc(4,15) = 0.000000000000000e+00 + Rwc(5,15) = 0.000000000000000e+00 Zws(5,15) = 0.000000000000000e+00 Rws(5,15) = 0.000000000000000e+00 Zwc(5,15) = 0.000000000000000e+00 + Rwc(6,15) = 0.000000000000000e+00 Zws(6,15) = 0.000000000000000e+00 Rws(6,15) = 0.000000000000000e+00 Zwc(6,15) = 0.000000000000000e+00 + Rwc(7,15) = 0.000000000000000e+00 Zws(7,15) = 0.000000000000000e+00 Rws(7,15) = 0.000000000000000e+00 Zwc(7,15) = 0.000000000000000e+00 + Rwc(8,15) = 0.000000000000000e+00 Zws(8,15) = 0.000000000000000e+00 Rws(8,15) = 0.000000000000000e+00 Zwc(8,15) = 0.000000000000000e+00 + Rwc(-8,16) = 0.000000000000000e+00 Zws(-8,16) = 0.000000000000000e+00 Rws(-8,16) = 0.000000000000000e+00 Zwc(-8,16) = 0.000000000000000e+00 + Rwc(-7,16) = 0.000000000000000e+00 Zws(-7,16) = 0.000000000000000e+00 Rws(-7,16) = 0.000000000000000e+00 Zwc(-7,16) = 0.000000000000000e+00 + Rwc(-6,16) = 0.000000000000000e+00 Zws(-6,16) = 0.000000000000000e+00 Rws(-6,16) = 0.000000000000000e+00 Zwc(-6,16) = 0.000000000000000e+00 + Rwc(-5,16) = 0.000000000000000e+00 Zws(-5,16) = 0.000000000000000e+00 Rws(-5,16) = 0.000000000000000e+00 Zwc(-5,16) = 0.000000000000000e+00 + Rwc(-4,16) = 0.000000000000000e+00 Zws(-4,16) = 0.000000000000000e+00 Rws(-4,16) = 0.000000000000000e+00 Zwc(-4,16) = 0.000000000000000e+00 + Rwc(-3,16) = 0.000000000000000e+00 Zws(-3,16) = 0.000000000000000e+00 Rws(-3,16) = 0.000000000000000e+00 Zwc(-3,16) = 0.000000000000000e+00 + Rwc(-2,16) = 0.000000000000000e+00 Zws(-2,16) = 0.000000000000000e+00 Rws(-2,16) = 0.000000000000000e+00 Zwc(-2,16) = 0.000000000000000e+00 + Rwc(-1,16) = 0.000000000000000e+00 Zws(-1,16) = 0.000000000000000e+00 Rws(-1,16) = 0.000000000000000e+00 Zwc(-1,16) = 0.000000000000000e+00 + Rwc(0,16) = 0.000000000000000e+00 Zws(0,16) = 0.000000000000000e+00 Rws(0,16) = 0.000000000000000e+00 Zwc(0,16) = 0.000000000000000e+00 + Rwc(1,16) = 0.000000000000000e+00 Zws(1,16) = 0.000000000000000e+00 Rws(1,16) = 0.000000000000000e+00 Zwc(1,16) = 0.000000000000000e+00 + Rwc(2,16) = 0.000000000000000e+00 Zws(2,16) = 0.000000000000000e+00 Rws(2,16) = 0.000000000000000e+00 Zwc(2,16) = 0.000000000000000e+00 + Rwc(3,16) = 0.000000000000000e+00 Zws(3,16) = 0.000000000000000e+00 Rws(3,16) = 0.000000000000000e+00 Zwc(3,16) = 0.000000000000000e+00 + Rwc(4,16) = 0.000000000000000e+00 Zws(4,16) = 0.000000000000000e+00 Rws(4,16) = 0.000000000000000e+00 Zwc(4,16) = 0.000000000000000e+00 + Rwc(5,16) = 0.000000000000000e+00 Zws(5,16) = 0.000000000000000e+00 Rws(5,16) = 0.000000000000000e+00 Zwc(5,16) = 0.000000000000000e+00 + Rwc(6,16) = 0.000000000000000e+00 Zws(6,16) = 0.000000000000000e+00 Rws(6,16) = 0.000000000000000e+00 Zwc(6,16) = 0.000000000000000e+00 + Rwc(7,16) = 0.000000000000000e+00 Zws(7,16) = 0.000000000000000e+00 Rws(7,16) = 0.000000000000000e+00 Zwc(7,16) = 0.000000000000000e+00 + Rwc(8,16) = 0.000000000000000e+00 Zws(8,16) = 0.000000000000000e+00 Rws(8,16) = 0.000000000000000e+00 Zwc(8,16) = 0.000000000000000e+00 + Vns(0,0) = 0.000000000000000e+00 Bns(0,0) = 0.000000000000000e+00 Vnc(0,0) = 2.317246691688746e-07 Bnc(0,0) = 0.000000000000000e+00 + Vns(1,0) = 3.615260745287559e-04 Bns(1,0) = 0.000000000000000e+00 Vnc(1,0) = -2.698542543180558e-02 Bnc(1,0) = 0.000000000000000e+00 + Vns(2,0) = 2.936732003365587e-03 Bns(2,0) = 0.000000000000000e+00 Vnc(2,0) = 1.581149044157859e-05 Bnc(2,0) = 0.000000000000000e+00 + Vns(3,0) = -7.408271395232943e-06 Bns(3,0) = 0.000000000000000e+00 Vnc(3,0) = 2.396997392962678e-04 Bnc(3,0) = 0.000000000000000e+00 + Vns(4,0) = -1.795222328947992e-05 Bns(4,0) = 0.000000000000000e+00 Vnc(4,0) = -2.019337447105605e-06 Bnc(4,0) = 0.000000000000000e+00 + Vns(5,0) = 4.290265999821032e-07 Bns(5,0) = 0.000000000000000e+00 Vnc(5,0) = -1.730974219643691e-06 Bnc(5,0) = 0.000000000000000e+00 + Vns(6,0) = 4.249690345269490e-07 Bns(6,0) = 0.000000000000000e+00 Vnc(6,0) = -3.805617702584517e-07 Bnc(6,0) = 0.000000000000000e+00 + Vns(7,0) = 3.069650515732713e-07 Bns(7,0) = 0.000000000000000e+00 Vnc(7,0) = -3.651256737107189e-07 Bnc(7,0) = 0.000000000000000e+00 + Vns(8,0) = 2.607577251001947e-07 Bns(8,0) = 0.000000000000000e+00 Vnc(8,0) = -3.390655286274187e-07 Bnc(8,0) = 0.000000000000000e+00 + Vns(-8,1) = -1.119503557792160e-07 Bns(-8,1) = 0.000000000000000e+00 Vnc(-8,1) = -9.880285524361096e-08 Bnc(-8,1) = 0.000000000000000e+00 + Vns(-7,1) = -1.291245677839528e-07 Bns(-7,1) = 0.000000000000000e+00 Vnc(-7,1) = -1.095288046407009e-07 Bnc(-7,1) = 0.000000000000000e+00 + Vns(-6,1) = -1.546312329965889e-07 Bns(-6,1) = 0.000000000000000e+00 Vnc(-6,1) = -9.800170288611064e-08 Bnc(-6,1) = 0.000000000000000e+00 + Vns(-5,1) = -6.137326913599088e-07 Bns(-5,1) = 0.000000000000000e+00 Vnc(-5,1) = -2.025184368494313e-07 Bnc(-5,1) = 0.000000000000000e+00 + Vns(-4,1) = 2.956999041205465e-07 Bns(-4,1) = 0.000000000000000e+00 Vnc(-4,1) = -5.118885419364452e-06 Bnc(-4,1) = 0.000000000000000e+00 + Vns(-3,1) = 4.967077899610324e-05 Bns(-3,1) = 0.000000000000000e+00 Vnc(-3,1) = 2.245749248326624e-07 Bnc(-3,1) = 0.000000000000000e+00 + Vns(-2,1) = 5.705164778307794e-05 Bns(-2,1) = 0.000000000000000e+00 Vnc(-2,1) = 4.070523669489626e-04 Bnc(-2,1) = 0.000000000000000e+00 + Vns(-1,1) = -1.404730296561205e-03 Bns(-1,1) = 0.000000000000000e+00 Vnc(-1,1) = 8.363065776412391e-04 Bnc(-1,1) = 0.000000000000000e+00 + Vns(0,1) = 3.635255498696447e-04 Bns(0,1) = 0.000000000000000e+00 Vnc(0,1) = 1.924871538367248e-04 Bnc(0,1) = 0.000000000000000e+00 + Vns(1,1) = -8.553644771982576e-03 Bns(1,1) = 0.000000000000000e+00 Vnc(1,1) = -9.185065397996847e-04 Bnc(1,1) = 0.000000000000000e+00 + Vns(2,1) = 7.069680204032500e-04 Bns(2,1) = 0.000000000000000e+00 Vnc(2,1) = -8.656907433540705e-04 Bnc(2,1) = 0.000000000000000e+00 + Vns(3,1) = 1.136205736268295e-04 Bns(3,1) = 0.000000000000000e+00 Vnc(3,1) = 1.620360094278575e-04 Bnc(3,1) = 0.000000000000000e+00 + Vns(4,1) = -2.232344225972295e-05 Bns(4,1) = 0.000000000000000e+00 Vnc(4,1) = 1.023356286585033e-05 Bnc(4,1) = 0.000000000000000e+00 + Vns(5,1) = -2.080646495255661e-07 Bns(5,1) = 0.000000000000000e+00 Vnc(5,1) = -2.802665732164414e-06 Bnc(5,1) = 0.000000000000000e+00 + Vns(6,1) = 4.515595458283027e-07 Bns(6,1) = 0.000000000000000e+00 Vnc(6,1) = -2.873379938581542e-07 Bnc(6,1) = 0.000000000000000e+00 + Vns(7,1) = 1.126997879573391e-07 Bns(7,1) = 0.000000000000000e+00 Vnc(7,1) = -1.552797580674565e-07 Bnc(7,1) = 0.000000000000000e+00 + Vns(8,1) = 8.947676420072433e-08 Bns(8,1) = 0.000000000000000e+00 Vnc(8,1) = -1.389371338748738e-07 Bnc(8,1) = 0.000000000000000e+00 + Vns(-8,2) = -3.404159416076152e-08 Bns(-8,2) = 0.000000000000000e+00 Vnc(-8,2) = 2.544810551159334e-08 Bnc(-8,2) = 0.000000000000000e+00 + Vns(-7,2) = -3.705194995991173e-08 Bns(-7,2) = 0.000000000000000e+00 Vnc(-7,2) = 2.990294999795196e-08 Bnc(-7,2) = 0.000000000000000e+00 + Vns(-6,2) = -3.484903645560261e-08 Bns(-6,2) = 0.000000000000000e+00 Vnc(-6,2) = 2.972496086939016e-08 Bnc(-6,2) = 0.000000000000000e+00 + Vns(-5,2) = -7.976875168033929e-09 Bns(-5,2) = 0.000000000000000e+00 Vnc(-5,2) = 1.129651769852410e-07 Bnc(-5,2) = 0.000000000000000e+00 + Vns(-4,2) = -5.412863863871793e-07 Bns(-4,2) = 0.000000000000000e+00 Vnc(-4,2) = 9.446813059386713e-07 Bnc(-4,2) = 0.000000000000000e+00 + Vns(-3,2) = -8.796203813021941e-06 Bns(-3,2) = 0.000000000000000e+00 Vnc(-3,2) = -3.483972616993959e-06 Bnc(-3,2) = 0.000000000000000e+00 + Vns(-2,2) = 7.196958530754774e-06 Bns(-2,2) = 0.000000000000000e+00 Vnc(-2,2) = -6.431843125380703e-05 Bnc(-2,2) = 0.000000000000000e+00 + Vns(-1,2) = 3.516238859906836e-04 Bns(-1,2) = 0.000000000000000e+00 Vnc(-1,2) = 1.062723084516331e-04 Bnc(-1,2) = 0.000000000000000e+00 + Vns(0,2) = -2.783351574149605e-03 Bns(0,2) = 0.000000000000000e+00 Vnc(0,2) = 3.726364328877747e-03 Bnc(0,2) = 0.000000000000000e+00 + Vns(1,2) = -9.434187126087586e-02 Bns(1,2) = 0.000000000000000e+00 Vnc(1,2) = -2.365375810384997e-02 Bnc(1,2) = 0.000000000000000e+00 + Vns(2,2) = 1.574778912468314e-03 Bns(2,2) = 0.000000000000000e+00 Vnc(2,2) = -1.598195669056470e-02 Bnc(2,2) = 0.000000000000000e+00 + Vns(3,2) = 1.534566714005057e-03 Bns(3,2) = 0.000000000000000e+00 Vnc(3,2) = 1.891819213268300e-04 Bnc(3,2) = 0.000000000000000e+00 + Vns(4,2) = -2.651491244226888e-05 Bns(4,2) = 0.000000000000000e+00 Vnc(4,2) = 1.247001738012563e-04 Bnc(4,2) = 0.000000000000000e+00 + Vns(5,2) = -1.146193619170696e-05 Bns(5,2) = 0.000000000000000e+00 Vnc(5,2) = -2.674491497357019e-06 Bnc(5,2) = 0.000000000000000e+00 + Vns(6,2) = -1.593678038213427e-07 Bns(6,2) = 0.000000000000000e+00 Vnc(6,2) = -7.672682706267660e-07 Bnc(6,2) = 0.000000000000000e+00 + Vns(7,2) = -1.187636675939020e-08 Bns(7,2) = 0.000000000000000e+00 Vnc(7,2) = -1.909109328154167e-08 Bnc(7,2) = 0.000000000000000e+00 + Vns(8,2) = -1.223958623257136e-08 Bns(8,2) = 0.000000000000000e+00 Vnc(8,2) = -4.325732757301675e-08 Bnc(8,2) = 0.000000000000000e+00 + Vns(-8,3) = 1.748973861302224e-09 Bns(-8,3) = 0.000000000000000e+00 Vnc(-8,3) = -6.618066033833917e-09 Bnc(-8,3) = 0.000000000000000e+00 + Vns(-7,3) = 1.568269633433805e-09 Bns(-7,3) = 0.000000000000000e+00 Vnc(-7,3) = -1.683596274685604e-10 Bnc(-7,3) = 0.000000000000000e+00 + Vns(-6,3) = -1.511219604726520e-08 Bns(-6,3) = 0.000000000000000e+00 Vnc(-6,3) = -7.032621581617273e-09 Bnc(-6,3) = 0.000000000000000e+00 + Vns(-5,3) = -2.562170973078682e-08 Bns(-5,3) = 0.000000000000000e+00 Vnc(-5,3) = -6.661876945102517e-08 Bnc(-5,3) = 0.000000000000000e+00 + Vns(-4,3) = 1.441358963519383e-06 Bns(-4,3) = 0.000000000000000e+00 Vnc(-4,3) = -6.525086555094152e-07 Bnc(-4,3) = 0.000000000000000e+00 + Vns(-3,3) = 2.763080050301650e-06 Bns(-3,3) = 0.000000000000000e+00 Vnc(-3,3) = 1.070575274050602e-05 Bnc(-3,3) = 0.000000000000000e+00 + Vns(-2,3) = -2.367644128135313e-05 Bns(-2,3) = 0.000000000000000e+00 Vnc(-2,3) = 9.652377535227475e-06 Bnc(-2,3) = 0.000000000000000e+00 + Vns(-1,3) = -1.269776831212886e-04 Bns(-1,3) = 0.000000000000000e+00 Vnc(-1,3) = 3.263068052549336e-05 Bnc(-1,3) = 0.000000000000000e+00 + Vns(0,3) = -1.342851271646695e-05 Bns(0,3) = 0.000000000000000e+00 Vnc(0,3) = -3.262535976177322e-04 Bnc(0,3) = 0.000000000000000e+00 + Vns(1,3) = 7.823686510004292e-03 Bns(1,3) = 0.000000000000000e+00 Vnc(1,3) = 1.532820703449856e-03 Bnc(1,3) = 0.000000000000000e+00 + Vns(2,3) = -2.446137694992827e-03 Bns(2,3) = 0.000000000000000e+00 Vnc(2,3) = -1.116356933616533e-04 Bnc(2,3) = 0.000000000000000e+00 + Vns(3,3) = -4.301070026282649e-06 Bns(3,3) = 0.000000000000000e+00 Vnc(3,3) = -1.401777418127623e-04 Bnc(3,3) = 0.000000000000000e+00 + Vns(4,3) = 2.551151721293338e-05 Bns(4,3) = 0.000000000000000e+00 Vnc(4,3) = 2.142590002000369e-05 Bnc(4,3) = 0.000000000000000e+00 + Vns(5,3) = 7.346359620913164e-07 Bns(5,3) = 0.000000000000000e+00 Vnc(5,3) = 5.195451259868035e-06 Bnc(5,3) = 0.000000000000000e+00 + Vns(6,3) = 5.636621318726241e-07 Bns(6,3) = 0.000000000000000e+00 Vnc(6,3) = 5.373156294941336e-07 Bnc(6,3) = 0.000000000000000e+00 + Vns(7,3) = 4.004967630069976e-08 Bns(7,3) = 0.000000000000000e+00 Vnc(7,3) = 7.437974492286376e-08 Bnc(7,3) = 0.000000000000000e+00 + Vns(8,3) = 8.256296314660044e-09 Bns(8,3) = 0.000000000000000e+00 Vnc(8,3) = 1.600071177546960e-09 Bnc(8,3) = 0.000000000000000e+00 + Vns(-8,4) = 6.689399374921839e-10 Bns(-8,4) = 0.000000000000000e+00 Vnc(-8,4) = 1.234826122703480e-09 Bnc(-8,4) = 0.000000000000000e+00 + Vns(-7,4) = -9.174076400312514e-10 Bns(-7,4) = 0.000000000000000e+00 Vnc(-7,4) = 8.463507993779618e-10 Bnc(-7,4) = 0.000000000000000e+00 + Vns(-6,4) = -2.722186455518994e-09 Bns(-6,4) = 0.000000000000000e+00 Vnc(-6,4) = -1.073176527192536e-09 Bnc(-6,4) = 0.000000000000000e+00 + Vns(-5,4) = -3.144424321054183e-08 Bns(-5,4) = 0.000000000000000e+00 Vnc(-5,4) = 2.122122960130627e-08 Bnc(-5,4) = 0.000000000000000e+00 + Vns(-4,4) = 8.752928470930517e-08 Bns(-4,4) = 0.000000000000000e+00 Vnc(-4,4) = -8.687605301807560e-07 Bnc(-4,4) = 0.000000000000000e+00 + Vns(-3,4) = 4.942998931363496e-06 Bns(-3,4) = 0.000000000000000e+00 Vnc(-3,4) = 3.055690729207285e-06 Bnc(-3,4) = 0.000000000000000e+00 + Vns(-2,4) = 2.109568711064377e-05 Bns(-2,4) = 0.000000000000000e+00 Vnc(-2,4) = 1.456379657473158e-05 Bnc(-2,4) = 0.000000000000000e+00 + Vns(-1,4) = 6.129113460964992e-05 Bns(-1,4) = 0.000000000000000e+00 Vnc(-1,4) = 1.219079946730962e-04 Bnc(-1,4) = 0.000000000000000e+00 + Vns(0,4) = -3.244304422711055e-04 Bns(0,4) = 0.000000000000000e+00 Vnc(0,4) = 2.042006690488081e-05 Bnc(0,4) = 0.000000000000000e+00 + Vns(1,4) = -8.454192761579013e-04 Bns(1,4) = 0.000000000000000e+00 Vnc(1,4) = -7.308804850015015e-04 Bnc(1,4) = 0.000000000000000e+00 + Vns(2,4) = 4.579752970188283e-04 Bns(2,4) = 0.000000000000000e+00 Vnc(2,4) = -1.195217517039685e-03 Bnc(2,4) = 0.000000000000000e+00 + Vns(3,4) = 3.273684486737393e-04 Bns(3,4) = 0.000000000000000e+00 Vnc(3,4) = -5.106007260046565e-04 Bnc(3,4) = 0.000000000000000e+00 + Vns(4,4) = 1.654187176394334e-04 Bns(4,4) = 0.000000000000000e+00 Vnc(4,4) = -4.532263732706203e-06 Bnc(4,4) = 0.000000000000000e+00 + Vns(5,4) = 4.726104629319321e-06 Bns(5,4) = 0.000000000000000e+00 Vnc(5,4) = 1.226802078008382e-05 Bnc(5,4) = 0.000000000000000e+00 + Vns(6,4) = -1.058271802978944e-08 Bns(6,4) = 0.000000000000000e+00 Vnc(6,4) = -5.461445567543923e-07 Bnc(6,4) = 0.000000000000000e+00 + Vns(7,4) = 6.324334037253599e-08 Bns(7,4) = 0.000000000000000e+00 Vnc(7,4) = -1.000878212904688e-07 Bnc(7,4) = 0.000000000000000e+00 + Vns(8,4) = 1.611570178132054e-08 Bns(8,4) = 0.000000000000000e+00 Vnc(8,4) = -1.321107744439072e-09 Bnc(8,4) = 0.000000000000000e+00 + Vns(-8,5) = -2.124117268492565e-09 Bns(-8,5) = 0.000000000000000e+00 Vnc(-8,5) = 7.322354754094777e-10 Bnc(-8,5) = 0.000000000000000e+00 + Vns(-7,5) = 3.829460194300499e-10 Bns(-7,5) = 0.000000000000000e+00 Vnc(-7,5) = -2.638313058255477e-09 Bnc(-7,5) = 0.000000000000000e+00 + Vns(-6,5) = 3.189246136044748e-09 Bns(-6,5) = 0.000000000000000e+00 Vnc(-6,5) = 1.158058330105362e-09 Bnc(-6,5) = 0.000000000000000e+00 + Vns(-5,5) = 2.206745925736097e-09 Bns(-5,5) = 0.000000000000000e+00 Vnc(-5,5) = 2.035837378878201e-08 Bnc(-5,5) = 0.000000000000000e+00 + Vns(-4,5) = -9.434002376782641e-08 Bns(-4,5) = 0.000000000000000e+00 Vnc(-4,5) = -4.049418821678991e-08 Bnc(-4,5) = 0.000000000000000e+00 + Vns(-3,5) = -3.503834026043367e-07 Bns(-3,5) = 0.000000000000000e+00 Vnc(-3,5) = -1.262418744590206e-07 Bnc(-3,5) = 0.000000000000000e+00 + Vns(-2,5) = -3.269265062991944e-06 Bns(-2,5) = 0.000000000000000e+00 Vnc(-2,5) = -3.073687550641196e-06 Bnc(-2,5) = 0.000000000000000e+00 + Vns(-1,5) = 7.806493561119298e-06 Bns(-1,5) = 0.000000000000000e+00 Vnc(-1,5) = 4.415087172571076e-06 Bnc(-1,5) = 0.000000000000000e+00 + Vns(0,5) = 4.228935930590499e-05 Bns(0,5) = 0.000000000000000e+00 Vnc(0,5) = 3.439918239962243e-05 Bnc(0,5) = 0.000000000000000e+00 + Vns(1,5) = -9.772534680364096e-05 Bns(1,5) = 0.000000000000000e+00 Vnc(1,5) = -1.863871654553932e-04 Bnc(1,5) = 0.000000000000000e+00 + Vns(2,5) = 1.138225177623875e-03 Bns(2,5) = 0.000000000000000e+00 Vnc(2,5) = 6.753139972650486e-04 Bnc(2,5) = 0.000000000000000e+00 + Vns(3,5) = -2.460048469538375e-04 Bns(3,5) = 0.000000000000000e+00 Vnc(3,5) = -3.989512832486962e-05 Bnc(3,5) = 0.000000000000000e+00 + Vns(4,5) = 4.728813987034984e-05 Bns(4,5) = 0.000000000000000e+00 Vnc(4,5) = -6.080619430279287e-05 Bnc(4,5) = 0.000000000000000e+00 + Vns(5,5) = 8.769536968427437e-06 Bns(5,5) = 0.000000000000000e+00 Vnc(5,5) = 5.462815397107864e-06 Bnc(5,5) = 0.000000000000000e+00 + Vns(6,5) = 1.460179361198880e-07 Bns(6,5) = 0.000000000000000e+00 Vnc(6,5) = 3.725320639849603e-07 Bnc(6,5) = 0.000000000000000e+00 + Vns(7,5) = -1.985195854599734e-10 Bns(7,5) = 0.000000000000000e+00 Vnc(7,5) = -1.340688420979832e-08 Bnc(7,5) = 0.000000000000000e+00 + Vns(8,5) = 4.025231202030501e-09 Bns(8,5) = 0.000000000000000e+00 Vnc(8,5) = 7.774994544223268e-10 Bnc(8,5) = 0.000000000000000e+00 + Vns(-8,6) = 9.616184386688651e-10 Bns(-8,6) = 0.000000000000000e+00 Vnc(-8,6) = -1.901677343612592e-09 Bnc(-8,6) = 0.000000000000000e+00 + Vns(-7,6) = 1.147704395419694e-09 Bns(-7,6) = 0.000000000000000e+00 Vnc(-7,6) = 1.104075482863391e-09 Bnc(-7,6) = 0.000000000000000e+00 + Vns(-6,6) = -1.492746908291086e-09 Bns(-6,6) = 0.000000000000000e+00 Vnc(-6,6) = -4.806205723473457e-11 Bnc(-6,6) = 0.000000000000000e+00 + Vns(-5,6) = 2.956541235578399e-09 Bns(-5,6) = 0.000000000000000e+00 Vnc(-5,6) = -3.630975267292798e-09 Bnc(-5,6) = 0.000000000000000e+00 + Vns(-4,6) = 2.390350286921727e-08 Bns(-4,6) = 0.000000000000000e+00 Vnc(-4,6) = 1.995280153301174e-08 Bnc(-4,6) = 0.000000000000000e+00 + Vns(-3,6) = 5.950771625016200e-08 Bns(-3,6) = 0.000000000000000e+00 Vnc(-3,6) = 7.883697301562045e-08 Bnc(-3,6) = 0.000000000000000e+00 + Vns(-2,6) = 6.827259758722723e-08 Bns(-2,6) = 0.000000000000000e+00 Vnc(-2,6) = 5.787371562444166e-07 Bnc(-2,6) = 0.000000000000000e+00 + Vns(-1,6) = -1.359979592425979e-06 Bns(-1,6) = 0.000000000000000e+00 Vnc(-1,6) = -1.012472714311664e-06 Bnc(-1,6) = 0.000000000000000e+00 + Vns(0,6) = -1.463542863258221e-06 Bns(0,6) = 0.000000000000000e+00 Vnc(0,6) = 3.549127793775028e-06 Bnc(0,6) = 0.000000000000000e+00 + Vns(1,6) = -1.680974032220132e-05 Bns(1,6) = 0.000000000000000e+00 Vnc(1,6) = -5.458439216277273e-06 Bnc(1,6) = 0.000000000000000e+00 + Vns(2,6) = -1.160792329716125e-04 Bns(2,6) = 0.000000000000000e+00 Vnc(2,6) = -9.810257111858198e-05 Bnc(2,6) = 0.000000000000000e+00 + Vns(3,6) = 2.246627430067221e-04 Bns(3,6) = 0.000000000000000e+00 Vnc(3,6) = 8.226264920723836e-05 Bnc(3,6) = 0.000000000000000e+00 + Vns(4,6) = 4.363879311184934e-06 Bns(4,6) = 0.000000000000000e+00 Vnc(4,6) = -5.516745502030442e-06 Bnc(4,6) = 0.000000000000000e+00 + Vns(5,6) = 8.025329873198597e-06 Bns(5,6) = 0.000000000000000e+00 Vnc(5,6) = 1.539902201917393e-06 Bnc(5,6) = 0.000000000000000e+00 + Vns(6,6) = 3.913723591502150e-07 Bns(6,6) = 0.000000000000000e+00 Vnc(6,6) = 8.478696893114640e-07 Bnc(6,6) = 0.000000000000000e+00 + Vns(7,6) = -2.619028503399582e-08 Bns(7,6) = 0.000000000000000e+00 Vnc(7,6) = 2.079303189093710e-08 Bnc(7,6) = 0.000000000000000e+00 + Vns(8,6) = 2.970531237881933e-09 Bns(8,6) = 0.000000000000000e+00 Vnc(8,6) = -1.273109710482015e-08 Bnc(8,6) = 0.000000000000000e+00 + Vns(-8,7) = 1.325146973705393e-09 Bns(-8,7) = 0.000000000000000e+00 Vnc(-8,7) = 1.167310804870840e-09 Bnc(-8,7) = 0.000000000000000e+00 + Vns(-7,7) = -1.353063266199082e-09 Bns(-7,7) = 0.000000000000000e+00 Vnc(-7,7) = 1.443098788516880e-09 Bnc(-7,7) = 0.000000000000000e+00 + Vns(-6,7) = -1.272093274186775e-09 Bns(-6,7) = 0.000000000000000e+00 Vnc(-6,7) = -1.437335456862542e-09 Bnc(-6,7) = 0.000000000000000e+00 + Vns(-5,7) = 1.179745435050612e-09 Bns(-5,7) = 0.000000000000000e+00 Vnc(-5,7) = -1.070025014442338e-09 Bnc(-5,7) = 0.000000000000000e+00 + Vns(-4,7) = -1.506885559989619e-09 Bns(-4,7) = 0.000000000000000e+00 Vnc(-4,7) = 3.495120773150787e-10 Bnc(-4,7) = 0.000000000000000e+00 + Vns(-3,7) = -5.394141746102647e-09 Bns(-3,7) = 0.000000000000000e+00 Vnc(-3,7) = -1.862529972937258e-08 Bnc(-3,7) = 0.000000000000000e+00 + Vns(-2,7) = 2.439442570562848e-08 Bns(-2,7) = 0.000000000000000e+00 Vnc(-2,7) = 3.256618179447843e-08 Bnc(-2,7) = 0.000000000000000e+00 + Vns(-1,7) = 6.248034207525545e-08 Bns(-1,7) = 0.000000000000000e+00 Vnc(-1,7) = 1.355473798133365e-07 Bnc(-1,7) = 0.000000000000000e+00 + Vns(0,7) = 2.921711295589671e-07 Bns(0,7) = 0.000000000000000e+00 Vnc(0,7) = -2.401150673976004e-07 Bnc(0,7) = 0.000000000000000e+00 + Vns(1,7) = 3.064741822879474e-06 Bns(1,7) = 0.000000000000000e+00 Vnc(1,7) = 1.813573475641358e-06 Bnc(1,7) = 0.000000000000000e+00 + Vns(2,7) = 9.598854278129385e-06 Bns(2,7) = 0.000000000000000e+00 Vnc(2,7) = 7.240476524187943e-06 Bnc(2,7) = 0.000000000000000e+00 + Vns(3,7) = 1.210567227285624e-05 Bns(3,7) = 0.000000000000000e+00 Vnc(3,7) = 2.523389474739557e-05 Bnc(3,7) = 0.000000000000000e+00 + Vns(4,7) = -5.157366332971406e-06 Bns(4,7) = 0.000000000000000e+00 Vnc(4,7) = 1.116976816135722e-06 Bnc(4,7) = 0.000000000000000e+00 + Vns(5,7) = 2.879215982580884e-06 Bns(5,7) = 0.000000000000000e+00 Vnc(5,7) = -2.847843038557103e-06 Bnc(5,7) = 0.000000000000000e+00 + Vns(6,7) = 7.818077655646012e-07 Bns(6,7) = 0.000000000000000e+00 Vnc(6,7) = 4.328679500530416e-07 Bnc(6,7) = 0.000000000000000e+00 + Vns(7,7) = -2.025152946630648e-09 Bns(7,7) = 0.000000000000000e+00 Vnc(7,7) = 5.169784127116141e-08 Bnc(7,7) = 0.000000000000000e+00 + Vns(8,7) = -2.228742437962250e-09 Bns(8,7) = 0.000000000000000e+00 Vnc(8,7) = -6.326777234031976e-09 Bnc(8,7) = 0.000000000000000e+00 + Vns(-8,8) = -2.163499640685374e-09 Bns(-8,8) = 0.000000000000000e+00 Vnc(-8,8) = 7.082179241850118e-10 Bnc(-8,8) = 0.000000000000000e+00 + Vns(-7,8) = 4.016798184930966e-10 Bns(-7,8) = 0.000000000000000e+00 Vnc(-7,8) = -2.794049405987697e-09 Bnc(-7,8) = 0.000000000000000e+00 + Vns(-6,8) = 2.875326698720669e-09 Bns(-6,8) = 0.000000000000000e+00 Vnc(-6,8) = 1.160502351130092e-09 Bnc(-6,8) = 0.000000000000000e+00 + Vns(-5,8) = -1.806249470698037e-09 Bns(-5,8) = 0.000000000000000e+00 Vnc(-5,8) = 2.409579604592275e-09 Bnc(-5,8) = 0.000000000000000e+00 + Vns(-4,8) = -1.455705638700814e-09 Bns(-4,8) = 0.000000000000000e+00 Vnc(-4,8) = -1.775022892486121e-09 Bnc(-4,8) = 0.000000000000000e+00 + Vns(-3,8) = 2.113969692371155e-09 Bns(-3,8) = 0.000000000000000e+00 Vnc(-3,8) = 5.375540580519743e-10 Bnc(-3,8) = 0.000000000000000e+00 + Vns(-2,8) = -3.203748321069098e-09 Bns(-2,8) = 0.000000000000000e+00 Vnc(-2,8) = -7.321026438402009e-09 Bnc(-2,8) = 0.000000000000000e+00 + Vns(-1,8) = -1.130398683957248e-08 Bns(-1,8) = 0.000000000000000e+00 Vnc(-1,8) = 1.620513028784322e-08 Bnc(-1,8) = 0.000000000000000e+00 + Vns(0,8) = -8.517950672269420e-08 Bns(0,8) = 0.000000000000000e+00 Vnc(0,8) = 2.311821844665134e-09 Bnc(0,8) = 0.000000000000000e+00 + Vns(1,8) = -4.378029993347842e-07 Bns(1,8) = 0.000000000000000e+00 Vnc(1,8) = -6.170597708140689e-07 Bnc(1,8) = 0.000000000000000e+00 + Vns(2,8) = -4.837602816597641e-07 Bns(2,8) = 0.000000000000000e+00 Vnc(2,8) = 3.921020007519920e-07 Bnc(2,8) = 0.000000000000000e+00 + Vns(3,8) = -4.842478574688790e-06 Bns(3,8) = 0.000000000000000e+00 Vnc(3,8) = -7.126503387691113e-06 Bnc(3,8) = 0.000000000000000e+00 + Vns(4,8) = 9.978986706838817e-06 Bns(4,8) = 0.000000000000000e+00 Vnc(4,8) = 8.080593481190059e-06 Bnc(4,8) = 0.000000000000000e+00 + Vns(5,8) = -9.115947315345141e-07 Bns(5,8) = 0.000000000000000e+00 Vnc(5,8) = 5.229548017007772e-07 Bnc(5,8) = 0.000000000000000e+00 + Vns(6,8) = 3.129109577685090e-07 Bns(6,8) = 0.000000000000000e+00 Vnc(6,8) = -1.526436324021198e-08 Bnc(6,8) = 0.000000000000000e+00 + Vns(7,8) = 1.474450097534304e-08 Bns(7,8) = 0.000000000000000e+00 Vnc(7,8) = 4.515312669266463e-08 Bnc(7,8) = 0.000000000000000e+00 + Vns(8,8) = -3.636845157622684e-09 Bns(8,8) = 0.000000000000000e+00 Vnc(8,8) = 3.416545954946963e-09 Bnc(8,8) = 0.000000000000000e+00 + Vns(-8,9) = 6.680652767491440e-10 Bns(-8,9) = 0.000000000000000e+00 Vnc(-8,9) = -1.788851249359865e-09 Bnc(-8,9) = 0.000000000000000e+00 + Vns(-7,9) = 8.988580004186863e-10 Bns(-7,9) = 0.000000000000000e+00 Vnc(-7,9) = 1.028889026350568e-09 Bnc(-7,9) = 0.000000000000000e+00 + Vns(-6,9) = -1.518893179759621e-09 Bns(-6,9) = 0.000000000000000e+00 Vnc(-6,9) = 1.052507698838195e-10 Bnc(-6,9) = 0.000000000000000e+00 + Vns(-5,9) = 1.684880347187310e-11 Bns(-5,9) = 0.000000000000000e+00 Vnc(-5,9) = -1.768379472809705e-09 Bnc(-5,9) = 0.000000000000000e+00 + Vns(-4,9) = 1.503930818158804e-09 Bns(-4,9) = 0.000000000000000e+00 Vnc(-4,9) = 2.270832979862210e-10 Bnc(-4,9) = 0.000000000000000e+00 + Vns(-3,9) = -7.016337922009143e-10 Bns(-3,9) = 0.000000000000000e+00 Vnc(-3,9) = 1.700420533407906e-09 Bnc(-3,9) = 0.000000000000000e+00 + Vns(-2,9) = -1.834766702722114e-09 Bns(-2,9) = 0.000000000000000e+00 Vnc(-2,9) = -4.865326320154538e-10 Bnc(-2,9) = 0.000000000000000e+00 + Vns(-1,9) = 3.140945822870872e-09 Bns(-1,9) = 0.000000000000000e+00 Vnc(-1,9) = -5.095084362155931e-09 Bnc(-1,9) = 0.000000000000000e+00 + Vns(0,9) = 1.209750345251340e-08 Bns(0,9) = 0.000000000000000e+00 Vnc(0,9) = 2.292247985458144e-08 Bnc(0,9) = 0.000000000000000e+00 + Vns(1,9) = 3.398692745872518e-08 Bns(1,9) = 0.000000000000000e+00 Vnc(1,9) = -9.310004894958913e-09 Bnc(1,9) = 0.000000000000000e+00 + Vns(2,9) = 1.547536771559106e-07 Bns(2,9) = 0.000000000000000e+00 Vnc(2,9) = 1.184548802158689e-07 Bnc(2,9) = 0.000000000000000e+00 + Vns(3,9) = 6.490800925138270e-07 Bns(3,9) = 0.000000000000000e+00 Vnc(3,9) = 1.160177125213857e-06 Bnc(3,9) = 0.000000000000000e+00 + Vns(4,9) = -1.385453156624095e-06 Bns(4,9) = 0.000000000000000e+00 Vnc(4,9) = -4.757817259715616e-07 Bnc(4,9) = 0.000000000000000e+00 + Vns(5,9) = 2.813820080282224e-07 Bns(5,9) = 0.000000000000000e+00 Vnc(5,9) = 4.319813650217982e-07 Bnc(5,9) = 0.000000000000000e+00 + Vns(6,9) = 7.474445480074858e-08 Bns(6,9) = 0.000000000000000e+00 Vnc(6,9) = -9.965957341859804e-08 Bnc(6,9) = 0.000000000000000e+00 + Vns(7,9) = 4.246423556723246e-08 Bns(7,9) = 0.000000000000000e+00 Vnc(7,9) = 2.756937132267946e-08 Bnc(7,9) = 0.000000000000000e+00 + Vns(8,9) = -3.741422739011887e-09 Bns(8,9) = 0.000000000000000e+00 Vnc(8,9) = 2.536769501550568e-09 Bnc(8,9) = 0.000000000000000e+00 + Vns(-8,10) = 1.905744980696546e-09 Bns(-8,10) = 0.000000000000000e+00 Vnc(-8,10) = 8.388274780217784e-10 Bnc(-8,10) = 0.000000000000000e+00 + Vns(-7,10) = -9.428207445628228e-10 Bns(-7,10) = 0.000000000000000e+00 Vnc(-7,10) = 2.037437327253169e-09 Bnc(-7,10) = 0.000000000000000e+00 + Vns(-6,10) = -1.446794072815982e-09 Bns(-6,10) = 0.000000000000000e+00 Vnc(-6,10) = -1.249761172292677e-09 Bnc(-6,10) = 0.000000000000000e+00 + Vns(-5,10) = 1.933152769908891e-09 Bns(-5,10) = 0.000000000000000e+00 Vnc(-5,10) = -7.945302928574952e-10 Bnc(-5,10) = 0.000000000000000e+00 + Vns(-4,10) = 2.466624074251998e-10 Bns(-4,10) = 0.000000000000000e+00 Vnc(-4,10) = 2.069305553301543e-09 Bnc(-4,10) = 0.000000000000000e+00 + Vns(-3,10) = -1.639217647496733e-09 Bns(-3,10) = 0.000000000000000e+00 Vnc(-3,10) = -7.946770941988317e-10 Bnc(-3,10) = 0.000000000000000e+00 + Vns(-2,10) = 1.869980767467910e-09 Bns(-2,10) = 0.000000000000000e+00 Vnc(-2,10) = -1.014784315225756e-09 Bnc(-2,10) = 0.000000000000000e+00 + Vns(-1,10) = 2.681352496912527e-10 Bns(-1,10) = 0.000000000000000e+00 Vnc(-1,10) = 2.334463035016854e-09 Bnc(-1,10) = 0.000000000000000e+00 + Vns(0,10) = -3.909907056238162e-09 Bns(0,10) = 0.000000000000000e+00 Vnc(0,10) = -9.617937217392395e-10 Bnc(0,10) = 0.000000000000000e+00 + Vns(1,10) = -4.858973068942073e-09 Bns(1,10) = 0.000000000000000e+00 Vnc(1,10) = -3.508085192479466e-09 Bnc(1,10) = 0.000000000000000e+00 + Vns(2,10) = -2.494817931453472e-08 Bns(2,10) = 0.000000000000000e+00 Vnc(2,10) = -3.971100592552121e-08 Bnc(2,10) = 0.000000000000000e+00 + Vns(3,10) = -4.725119090216250e-08 Bns(3,10) = 0.000000000000000e+00 Vnc(3,10) = -5.085710207040193e-08 Bnc(3,10) = 0.000000000000000e+00 + Vns(4,10) = -4.189231850885840e-09 Bns(4,10) = 0.000000000000000e+00 Vnc(4,10) = -2.651064258720780e-07 Bnc(4,10) = 0.000000000000000e+00 + Vns(5,10) = 2.165142392953849e-07 Bns(5,10) = 0.000000000000000e+00 Vnc(5,10) = 3.691606466986303e-07 Bnc(5,10) = 0.000000000000000e+00 + Vns(6,10) = -8.167397178043019e-08 Bns(6,10) = 0.000000000000000e+00 Vnc(6,10) = 4.074241067735015e-08 Bnc(6,10) = 0.000000000000000e+00 + Vns(7,10) = 1.132147698064728e-08 Bns(7,10) = 0.000000000000000e+00 Vnc(7,10) = -1.057413993831165e-08 Bnc(7,10) = 0.000000000000000e+00 + Vns(8,10) = 2.632033782638389e-09 Bns(8,10) = 0.000000000000000e+00 Vnc(8,10) = 2.407569141854182e-09 Bnc(8,10) = 0.000000000000000e+00 + Vns(-8,11) = -2.348589922121108e-09 Bns(-8,11) = 0.000000000000000e+00 Vnc(-8,11) = 8.362209827057898e-10 Bnc(-8,11) = 0.000000000000000e+00 + Vns(-7,11) = -3.644315408432629e-11 Bns(-7,11) = 0.000000000000000e+00 Vnc(-7,11) = -3.190903840610664e-09 Bnc(-7,11) = 0.000000000000000e+00 + Vns(-6,11) = 3.088299636533910e-09 Bns(-6,11) = 0.000000000000000e+00 Vnc(-6,11) = 6.457285804902274e-10 Bnc(-6,11) = 0.000000000000000e+00 + Vns(-5,11) = -1.592434480714765e-09 Bns(-5,11) = 0.000000000000000e+00 Vnc(-5,11) = 2.414666347394858e-09 Bnc(-5,11) = 0.000000000000000e+00 + Vns(-4,11) = -1.903482407175851e-09 Bns(-4,11) = 0.000000000000000e+00 Vnc(-4,11) = -2.182974883141301e-09 Bnc(-4,11) = 0.000000000000000e+00 + Vns(-3,11) = 2.029788224741246e-09 Bns(-3,11) = 0.000000000000000e+00 Vnc(-3,11) = -1.079590846888250e-09 Bnc(-3,11) = 0.000000000000000e+00 + Vns(-2,11) = 4.710256066857180e-11 Bns(-2,11) = 0.000000000000000e+00 Vnc(-2,11) = 1.585578407768542e-09 Bnc(-2,11) = 0.000000000000000e+00 + Vns(-1,11) = -1.182598442545499e-09 Bns(-1,11) = 0.000000000000000e+00 Vnc(-1,11) = -8.438175274172322e-10 Bnc(-1,11) = 0.000000000000000e+00 + Vns(0,11) = 1.587718340942991e-09 Bns(0,11) = 0.000000000000000e+00 Vnc(0,11) = -9.184167130805846e-10 Bnc(0,11) = 0.000000000000000e+00 + Vns(1,11) = 1.622882527575912e-09 Bns(1,11) = 0.000000000000000e+00 Vnc(1,11) = 1.820906154524605e-09 Bnc(1,11) = 0.000000000000000e+00 + Vns(2,11) = 1.495925317781351e-09 Bns(2,11) = 0.000000000000000e+00 Vnc(2,11) = 5.841433501528461e-09 Bnc(2,11) = 0.000000000000000e+00 + Vns(3,11) = 5.370263470159196e-09 Bns(3,11) = 0.000000000000000e+00 Vnc(3,11) = 1.696829105074453e-09 Bnc(3,11) = 0.000000000000000e+00 + Vns(4,11) = 1.421419822297165e-08 Bns(4,11) = 0.000000000000000e+00 Vnc(4,11) = 7.729514665183278e-08 Bnc(4,11) = 0.000000000000000e+00 + Vns(5,11) = -8.552919341262831e-08 Bns(5,11) = 0.000000000000000e+00 Vnc(5,11) = -1.102889614415282e-07 Bnc(5,11) = 0.000000000000000e+00 + Vns(6,11) = 2.876299383890691e-08 Bns(6,11) = 0.000000000000000e+00 Vnc(6,11) = 4.146225201478800e-08 Bnc(6,11) = 0.000000000000000e+00 + Vns(7,11) = -1.998083976986077e-09 Bns(7,11) = 0.000000000000000e+00 Vnc(7,11) = -3.880116202190206e-09 Bnc(7,11) = 0.000000000000000e+00 + Vns(8,11) = 3.679245844091589e-09 Bns(8,11) = 0.000000000000000e+00 Vnc(8,11) = 2.214904633533511e-09 Bnc(8,11) = 0.000000000000000e+00 + Vns(-8,12) = 4.078230526558530e-10 Bns(-8,12) = 0.000000000000000e+00 Vnc(-8,12) = -1.321481459959656e-09 Bnc(-8,12) = 0.000000000000000e+00 + Vns(-7,12) = 8.906264297455852e-10 Bns(-7,12) = 0.000000000000000e+00 Vnc(-7,12) = 1.045533004078315e-09 Bnc(-7,12) = 0.000000000000000e+00 + Vns(-6,12) = -1.363657954744246e-09 Bns(-6,12) = 0.000000000000000e+00 Vnc(-6,12) = 6.570395418624727e-10 Bnc(-6,12) = 0.000000000000000e+00 + Vns(-5,12) = -4.600401116227537e-10 Bns(-5,12) = 0.000000000000000e+00 Vnc(-5,12) = -1.438474336087157e-09 Bnc(-5,12) = 0.000000000000000e+00 + Vns(-4,12) = 1.548824920363380e-09 Bns(-4,12) = 0.000000000000000e+00 Vnc(-4,12) = -1.502225069898190e-10 Bnc(-4,12) = 0.000000000000000e+00 + Vns(-3,12) = -3.378006618774399e-11 Bns(-3,12) = 0.000000000000000e+00 Vnc(-3,12) = 1.984416045312277e-09 Bnc(-3,12) = 0.000000000000000e+00 + Vns(-2,12) = -2.249981170372708e-09 Bns(-2,12) = 0.000000000000000e+00 Vnc(-2,12) = 6.447261024706818e-11 Bnc(-2,12) = 0.000000000000000e+00 + Vns(-1,12) = 1.377852839010154e-10 Bns(-1,12) = 0.000000000000000e+00 Vnc(-1,12) = -1.963603343386443e-09 Bnc(-1,12) = 0.000000000000000e+00 + Vns(0,12) = 1.553020881146861e-09 Bns(0,12) = 0.000000000000000e+00 Vnc(0,12) = 9.324803700228478e-10 Bnc(0,12) = 0.000000000000000e+00 + Vns(1,12) = -1.740191328292984e-09 Bns(1,12) = 0.000000000000000e+00 Vnc(1,12) = 1.249372263279825e-09 Bnc(1,12) = 0.000000000000000e+00 + Vns(2,12) = -1.436488612432634e-09 Bns(2,12) = 0.000000000000000e+00 Vnc(2,12) = -2.112069224104039e-09 Bnc(2,12) = 0.000000000000000e+00 + Vns(3,12) = 7.223698036831810e-10 Bns(3,12) = 0.000000000000000e+00 Vnc(3,12) = -1.856432009734869e-09 Bnc(3,12) = 0.000000000000000e+00 + Vns(4,12) = -2.999136554380044e-09 Bns(4,12) = 0.000000000000000e+00 Vnc(4,12) = -7.707856609289439e-09 Bnc(4,12) = 0.000000000000000e+00 + Vns(5,12) = 9.538644208539547e-09 Bns(5,12) = 0.000000000000000e+00 Vnc(5,12) = 2.617186306852843e-09 Bnc(5,12) = 0.000000000000000e+00 + Vns(6,12) = -3.206237041430194e-10 Bns(6,12) = 0.000000000000000e+00 Vnc(6,12) = 4.486015866015021e-09 Bnc(6,12) = 0.000000000000000e+00 + Vns(7,12) = -3.888634685136580e-09 Bns(7,12) = 0.000000000000000e+00 Vnc(7,12) = 5.762347729762052e-09 Bnc(7,12) = 0.000000000000000e+00 + Vns(8,12) = -2.403600525866908e-09 Bns(8,12) = 0.000000000000000e+00 Vnc(8,12) = -1.830580082770829e-09 Bnc(8,12) = 0.000000000000000e+00 + Vns(-8,13) = 2.252731224037186e-09 Bns(-8,13) = 0.000000000000000e+00 Vnc(-8,13) = 2.138617513556780e-10 Bnc(-8,13) = 0.000000000000000e+00 + Vns(-7,13) = -4.482553888921651e-10 Bns(-7,13) = 0.000000000000000e+00 Vnc(-7,13) = 2.155247526246393e-09 Bnc(-7,13) = 0.000000000000000e+00 + Vns(-6,13) = -1.639732670637881e-09 Bns(-6,13) = 0.000000000000000e+00 Vnc(-6,13) = -1.028574295931528e-09 Bnc(-6,13) = 0.000000000000000e+00 + Vns(-5,13) = 1.847263795554893e-09 Bns(-5,13) = 0.000000000000000e+00 Vnc(-5,13) = -1.083201339592199e-09 Bnc(-5,13) = 0.000000000000000e+00 + Vns(-4,13) = 3.829507198858811e-10 Bns(-4,13) = 0.000000000000000e+00 Vnc(-4,13) = 1.994246568175481e-09 Bnc(-4,13) = 0.000000000000000e+00 + Vns(-3,13) = -1.764041073123140e-09 Bns(-3,13) = 0.000000000000000e+00 Vnc(-3,13) = -9.098591894767913e-10 Bnc(-3,13) = 0.000000000000000e+00 + Vns(-2,13) = 2.247444882086267e-09 Bns(-2,13) = 0.000000000000000e+00 Vnc(-2,13) = -1.633851252457388e-09 Bnc(-2,13) = 0.000000000000000e+00 + Vns(-1,13) = 1.249670756056647e-09 Bns(-1,13) = 0.000000000000000e+00 Vnc(-1,13) = 2.793533319299517e-09 Bnc(-1,13) = 0.000000000000000e+00 + Vns(0,13) = -2.790868075034391e-09 Bns(0,13) = 0.000000000000000e+00 Vnc(0,13) = 1.555037546645509e-10 Bnc(0,13) = 0.000000000000000e+00 + Vns(1,13) = 1.083208413766244e-09 Bns(1,13) = 0.000000000000000e+00 Vnc(1,13) = -2.857895227626469e-09 Bnc(1,13) = 0.000000000000000e+00 + Vns(2,13) = 2.751952458716872e-09 Bns(2,13) = 0.000000000000000e+00 Vnc(2,13) = 1.610841993667792e-09 Bnc(2,13) = 0.000000000000000e+00 + Vns(3,13) = -1.396348650374669e-09 Bns(3,13) = 0.000000000000000e+00 Vnc(3,13) = 2.177498835695045e-09 Bnc(3,13) = 0.000000000000000e+00 + Vns(4,13) = -3.193331714647574e-10 Bns(4,13) = 0.000000000000000e+00 Vnc(4,13) = -1.267119618534420e-09 Bnc(4,13) = 0.000000000000000e+00 + Vns(5,13) = 4.244405468625336e-10 Bns(5,13) = 0.000000000000000e+00 Vnc(5,13) = 2.732888553582946e-09 Bnc(5,13) = 0.000000000000000e+00 + Vns(6,13) = -2.177028298675602e-09 Bns(6,13) = 0.000000000000000e+00 Vnc(6,13) = -5.702366039155357e-09 Bnc(6,13) = 0.000000000000000e+00 + Vns(7,13) = 8.282361984504134e-10 Bns(7,13) = 0.000000000000000e+00 Vnc(7,13) = 1.179423058970364e-09 Bnc(7,13) = 0.000000000000000e+00 + Vns(8,13) = 9.529204423548076e-10 Bns(8,13) = 0.000000000000000e+00 Vnc(8,13) = -4.766889031473332e-10 Bnc(8,13) = 0.000000000000000e+00 + Vns(-8,14) = -2.692019287623531e-09 Bns(-8,14) = 0.000000000000000e+00 Vnc(-8,14) = 1.085204364454131e-09 Bnc(-8,14) = 0.000000000000000e+00 + Vns(-7,14) = -6.435967757157091e-10 Bns(-7,14) = 0.000000000000000e+00 Vnc(-7,14) = -3.219010817120577e-09 Bnc(-7,14) = 0.000000000000000e+00 + Vns(-6,14) = 2.773128693320108e-09 Bns(-6,14) = 0.000000000000000e+00 Vnc(-6,14) = 1.638612630905800e-10 Bnc(-6,14) = 0.000000000000000e+00 + Vns(-5,14) = -1.263977965959629e-09 Bns(-5,14) = 0.000000000000000e+00 Vnc(-5,14) = 2.402266490898565e-09 Bnc(-5,14) = 0.000000000000000e+00 + Vns(-4,14) = -2.024955241362091e-09 Bns(-4,14) = 0.000000000000000e+00 Vnc(-4,14) = -1.507729566836580e-09 Bnc(-4,14) = 0.000000000000000e+00 + Vns(-3,14) = 1.217094771210776e-09 Bns(-3,14) = 0.000000000000000e+00 Vnc(-3,14) = -9.482121941275382e-10 Bnc(-3,14) = 0.000000000000000e+00 + Vns(-2,14) = -2.687709194659331e-10 Bns(-2,14) = 0.000000000000000e+00 Vnc(-2,14) = 1.296993608545786e-09 Bnc(-2,14) = 0.000000000000000e+00 + Vns(-1,14) = -1.437625148793070e-09 Bns(-1,14) = 0.000000000000000e+00 Vnc(-1,14) = -7.016754088281401e-10 Bnc(-1,14) = 0.000000000000000e+00 + Vns(0,14) = 8.562155820363918e-10 Bns(0,14) = 0.000000000000000e+00 Vnc(0,14) = -1.064476267389838e-09 Bnc(0,14) = 0.000000000000000e+00 + Vns(1,14) = 6.281563588209551e-10 Bns(1,14) = 0.000000000000000e+00 Vnc(1,14) = 1.112082814545712e-09 Bnc(1,14) = 0.000000000000000e+00 + Vns(2,14) = -1.567431323930155e-09 Bns(2,14) = 0.000000000000000e+00 Vnc(2,14) = 4.771939874540105e-10 Bnc(2,14) = 0.000000000000000e+00 + Vns(3,14) = -5.904372019586187e-10 Bns(3,14) = 0.000000000000000e+00 Vnc(3,14) = -1.975906584469984e-09 Bnc(3,14) = 0.000000000000000e+00 + Vns(4,14) = 2.013412261766343e-09 Bns(4,14) = 0.000000000000000e+00 Vnc(4,14) = -7.286210839942208e-10 Bnc(4,14) = 0.000000000000000e+00 + Vns(5,14) = 4.355512646926134e-10 Bns(5,14) = 0.000000000000000e+00 Vnc(5,14) = 1.351876475804893e-09 Bnc(5,14) = 0.000000000000000e+00 + Vns(6,14) = -1.647817897125279e-09 Bns(6,14) = 0.000000000000000e+00 Vnc(6,14) = 8.725413132387147e-10 Bnc(6,14) = 0.000000000000000e+00 + Vns(7,14) = 3.929972341060488e-10 Bns(7,14) = 0.000000000000000e+00 Vnc(7,14) = -2.637652702527251e-09 Bnc(7,14) = 0.000000000000000e+00 + Vns(8,14) = 1.154349091999697e-09 Bns(8,14) = 0.000000000000000e+00 Vnc(8,14) = 1.583357544661288e-09 Bnc(8,14) = 0.000000000000000e+00 + Vns(-8,15) = 4.044523274174937e-10 Bns(-8,15) = 0.000000000000000e+00 Vnc(-8,15) = -1.142470695626345e-09 Bnc(-8,15) = 0.000000000000000e+00 + Vns(-7,15) = 1.105781957568541e-09 Bns(-7,15) = 0.000000000000000e+00 Vnc(-7,15) = 1.047800959854013e-09 Bnc(-7,15) = 0.000000000000000e+00 + Vns(-6,15) = -1.168179586966490e-09 Bns(-6,15) = 0.000000000000000e+00 Vnc(-6,15) = 1.028070372879905e-09 Bnc(-6,15) = 0.000000000000000e+00 + Vns(-5,15) = -7.244451087006072e-10 Bns(-5,15) = 0.000000000000000e+00 Vnc(-5,15) = -1.212100823435358e-09 Bnc(-5,15) = 0.000000000000000e+00 + Vns(-4,15) = 1.714104894737665e-09 Bns(-4,15) = 0.000000000000000e+00 Vnc(-4,15) = -5.256658865724289e-10 Bnc(-4,15) = 0.000000000000000e+00 + Vns(-3,15) = 7.618027823985214e-10 Bns(-3,15) = 0.000000000000000e+00 Vnc(-3,15) = 2.084637707903561e-09 Bnc(-3,15) = 0.000000000000000e+00 + Vns(-2,15) = -1.941893777786765e-09 Bns(-2,15) = 0.000000000000000e+00 Vnc(-2,15) = 7.891572360726379e-10 Bnc(-2,15) = 0.000000000000000e+00 + Vns(-1,15) = 1.575063123606806e-11 Bns(-1,15) = 0.000000000000000e+00 Vnc(-1,15) = -1.827518573558312e-09 Bnc(-1,15) = 0.000000000000000e+00 + Vns(0,15) = 2.084298676156183e-09 Bns(0,15) = 0.000000000000000e+00 Vnc(0,15) = 8.415594507286272e-10 Bnc(0,15) = 0.000000000000000e+00 + Vns(1,15) = -1.243359149091746e-09 Bns(1,15) = 0.000000000000000e+00 Vnc(1,15) = 1.868316416793549e-09 Bnc(1,15) = 0.000000000000000e+00 + Vns(2,15) = -8.583004143792248e-10 Bns(2,15) = 0.000000000000000e+00 Vnc(2,15) = -1.607730290751592e-09 Bnc(2,15) = 0.000000000000000e+00 + Vns(3,15) = 2.141495457872983e-09 Bns(3,15) = 0.000000000000000e+00 Vnc(3,15) = 4.351742046621711e-10 Bnc(3,15) = 0.000000000000000e+00 + Vns(4,15) = -1.405155123083525e-09 Bns(4,15) = 0.000000000000000e+00 Vnc(4,15) = 2.429146198494925e-09 Bnc(4,15) = 0.000000000000000e+00 + Vns(5,15) = -1.833982643109855e-09 Bns(5,15) = 0.000000000000000e+00 Vnc(5,15) = -2.043803519889887e-09 Bnc(5,15) = 0.000000000000000e+00 + Vns(6,15) = 2.796498580832065e-09 Bns(6,15) = 0.000000000000000e+00 Vnc(6,15) = -9.225918945820840e-10 Bnc(6,15) = 0.000000000000000e+00 + Vns(7,15) = 2.996996179755500e-10 Bns(7,15) = 0.000000000000000e+00 Vnc(7,15) = 3.017399638344682e-09 Bnc(7,15) = 0.000000000000000e+00 + Vns(8,15) = -2.631105731137864e-09 Bns(8,15) = 0.000000000000000e+00 Vnc(8,15) = -3.553071888423236e-10 Bnc(8,15) = 0.000000000000000e+00 + Vns(-8,16) = 2.349710031488881e-09 Bns(-8,16) = 0.000000000000000e+00 Vnc(-8,16) = -5.502839457571293e-10 Bnc(-8,16) = 0.000000000000000e+00 + Vns(-7,16) = -2.530543907232820e-10 Bns(-7,16) = 0.000000000000000e+00 Vnc(-7,16) = 2.052078284856503e-09 Bnc(-7,16) = 0.000000000000000e+00 + Vns(-6,16) = -1.833440707575433e-09 Bns(-6,16) = 0.000000000000000e+00 Vnc(-6,16) = -1.110755470826847e-09 Bnc(-6,16) = 0.000000000000000e+00 + Vns(-5,16) = 1.712782972648504e-09 Bns(-5,16) = 0.000000000000000e+00 Vnc(-5,16) = -1.173643381240079e-09 Bnc(-5,16) = 0.000000000000000e+00 + Vns(-4,16) = 1.341164476913825e-10 Bns(-4,16) = 0.000000000000000e+00 Vnc(-4,16) = 2.058900587638291e-09 Bnc(-4,16) = 0.000000000000000e+00 + Vns(-3,16) = -2.199191584628903e-09 Bns(-3,16) = 0.000000000000000e+00 Vnc(-3,16) = -1.239536391685439e-09 Bnc(-3,16) = 0.000000000000000e+00 + Vns(-2,16) = 2.184497533410610e-09 Bns(-2,16) = 0.000000000000000e+00 Vnc(-2,16) = -2.215085254422409e-09 Bnc(-2,16) = 0.000000000000000e+00 + Vns(-1,16) = 1.568620491142703e-09 Bns(-1,16) = 0.000000000000000e+00 Vnc(-1,16) = 2.491927822264839e-09 Bnc(-1,16) = 0.000000000000000e+00 + Vns(0,16) = -2.719178936154893e-09 Bns(0,16) = 0.000000000000000e+00 Vnc(0,16) = 2.959201066204409e-10 Bnc(0,16) = 0.000000000000000e+00 + Vns(1,16) = 5.139718480912930e-10 Bns(1,16) = 0.000000000000000e+00 Vnc(1,16) = -3.003161430482166e-09 Bnc(1,16) = 0.000000000000000e+00 + Vns(2,16) = 2.422902263104706e-09 Bns(2,16) = 0.000000000000000e+00 Vnc(2,16) = 6.505924972582692e-10 Bnc(2,16) = 0.000000000000000e+00 + Vns(3,16) = -9.809720649166398e-10 Bns(3,16) = 0.000000000000000e+00 Vnc(3,16) = 1.212005999942047e-09 Bnc(3,16) = 0.000000000000000e+00 + Vns(4,16) = -3.991979692262652e-10 Bns(4,16) = 0.000000000000000e+00 Vnc(4,16) = -1.517758084832128e-09 Bnc(4,16) = 0.000000000000000e+00 + Vns(5,16) = 1.381787508115467e-09 Bns(5,16) = 0.000000000000000e+00 Vnc(5,16) = -6.406491230336265e-11 Bnc(5,16) = 0.000000000000000e+00 + Vns(6,16) = -4.777115243490720e-10 Bns(6,16) = 0.000000000000000e+00 Vnc(6,16) = 8.960573124051458e-10 Bnc(6,16) = 0.000000000000000e+00 + Vns(7,16) = -9.862419498598255e-10 Bns(7,16) = 0.000000000000000e+00 Vnc(7,16) = -1.064378690033439e-09 Bnc(7,16) = 0.000000000000000e+00 + Vns(8,16) = 1.192438451588155e-09 Bns(8,16) = 0.000000000000000e+00 Vnc(8,16) = -1.275736108942888e-09 Bnc(8,16) = 0.000000000000000e+00 +/ +&numericlist + linitialize = 0 , + ndiscrete = 2 , + nquad = -1 , + impol = -4 , + intor = -4 , + lsparse = 0 , + lsvdiota = 0 , + imethod = 3 , + iorder = 2 , + iprecon = 1 , + iotatol = -1.00000 , + lextrap = 0 , + mregular = -1 , +/ +&locallist + lbeltrami = 4 , + linitgues = 1 , + lposdef = 0 , +/ +&globallist + lfindzero = 2 , + escale = 0.0000000 , + opsilon = 1.0000000 , + pcondense = 2.0000000 , + epsilon = 0.0000000 , + wpoloidal = 0.0000000 , + upsilon = 1.0000000 , + forcetol = 1.00000e-12 , + c05xtol = 1.00000e-12 , + c05factor = 0.00100000 , + lreadgf = T , + mfreeits = 0 , + gbntol = 1.00000e-06 , + gbnbld = 0.66600000 , + vcasingtol = 0.00010000000 , + vcasingits = 8 , +/ +&diagnosticslist + odetol = 1.00000e-10 , + nppts = 22 , + lhevalues = F , + lhevectors = F , + lperturbed = 0 , + dpp = -1 , + dqq = -1 , + lcheck = 0 , + ltiming = F , + nPtrj = -1 -1 -1 +/ +&screenlist + WPP00AA = T , +/ + 0 0 3.004488684791879e+00 0.000000000000000e+00 0.000000000000000e+00 6.118998002028783e-04 3.000891568182305e+00 0.000000000000000e+00 0.000000000000000e+00 1.977985854662758e-04 + 0 1 1.365073859620334e-03 -5.830689692513589e-02 2.947540050747188e-02 4.715727723889635e-04 1.549584617912061e-04 -5.980912307968718e-02 2.988418381825432e-02 8.986339895053900e-05 + 0 2 3.666434349455090e-05 3.431655356338431e-05 3.359375977515831e-05 -7.669406382682778e-06 1.079033239987434e-05 9.295363299317108e-06 1.131277816928571e-05 -6.310284361237698e-06 + 0 3 7.044470487901112e-08 -1.047619548330291e-06 -4.104302050657725e-06 4.578468831003639e-06 4.824230315394798e-07 -1.153962330568762e-07 -1.560321108793688e-06 1.715870237613191e-06 + 0 4 -2.392268224441195e-07 -2.092093619767756e-07 -5.825051059223159e-08 -3.121180301188012e-08 -1.396359657407556e-07 -1.153155943646326e-07 -1.806451705037117e-08 -4.040969821267141e-08 + 0 5 1.806068832299088e-08 1.205358949141715e-08 2.670867606562417e-08 -8.762127336805281e-09 2.017099127964016e-08 1.401507246269720e-08 2.414830238655452e-08 -9.527831267414959e-09 + 0 6 6.813757185026548e-09 -2.270920907905205e-11 8.745481542290886e-09 -9.898481314273818e-10 6.925434265595456e-09 -1.303343366244095e-11 8.069397553975789e-09 -1.037418766943065e-09 + 0 7 3.568419825189332e-09 -9.110926079797283e-10 5.195115920721118e-09 -2.625645209575288e-10 3.675422456724106e-09 -1.113269211802120e-09 5.259989253996036e-09 -6.387169047049453e-10 + 0 8 2.277053272246763e-09 -4.681839255681734e-10 3.290460660326865e-09 -1.748351864582697e-10 2.448119131401051e-09 -7.268914318436959e-10 3.665286766753462e-09 -1.623395000235289e-10 + 1 -8 1.133178635095641e-09 -8.066452413206522e-10 -1.364844805890174e-09 -1.388870888468150e-09 2.728126549827848e-09 -1.918785786242337e-09 -3.314765448552290e-09 -3.478356622638992e-09 + 1 -7 1.706089334635382e-09 -1.349015488454131e-09 -1.730165178440360e-09 -2.144963859818259e-09 3.940008580407160e-09 -3.278866514409701e-09 -3.796826165502953e-09 -5.285499205832392e-09 + 1 -6 2.432351473730271e-09 -2.053832305579850e-09 -2.338044348142431e-09 -3.067478512364836e-09 5.486921553165711e-09 -4.688779096258160e-09 -5.034738592542716e-09 -7.153942044142097e-09 + 1 -5 4.096113055003505e-09 -2.415680310007413e-09 -7.421869232096024e-09 -7.042271590230728e-09 7.865427799766353e-09 -5.127008266028507e-09 -1.464611671305431e-08 -1.569337198542345e-08 + 1 -4 -8.221575853912973e-08 5.574174595270495e-08 3.161407760062370e-09 -2.339600910414968e-08 -1.674938050731462e-07 1.342466047628991e-07 8.428990513324408e-11 -3.362675003302608e-08 + 1 -3 7.731451342321821e-08 -1.091078013401058e-07 1.948846323361565e-06 1.819434511802310e-06 1.216780438593311e-07 -1.349823049404980e-07 4.011485145733234e-06 3.893088440686576e-06 + 1 -2 5.120478870378251e-05 -5.181264520870208e-05 -3.140772379105382e-07 -1.083604150627331e-06 1.043858713065171e-04 -1.054589289336694e-04 -5.487316991492441e-07 -7.603827742293734e-07 + 1 -1 -1.261186838089419e-05 7.855678509913993e-06 -1.346851358151320e-03 -1.349234482420350e-03 -1.784340720809674e-05 -4.035719978185197e-06 -2.691252596101455e-03 -2.703990464548454e-03 + 1 0 1.494550543821415e-01 -1.493756835429122e-01 1.801558741731371e-04 -1.855615020454945e-04 2.985157256456206e-01 -2.982171224132523e-01 4.251806119524189e-04 -4.273361484487426e-04 + 1 1 -2.490804925158836e-02 -2.488407885807470e-02 1.254008222089360e-02 5.899260950302775e-05 -4.974830093880376e-02 -4.974775916588486e-02 2.502603653136018e-02 1.198105172091764e-04 + 1 2 1.765983829666361e-05 5.205162002122834e-06 2.430024505166750e-05 -2.354776829548254e-05 3.483764396893520e-05 9.183647372250326e-06 6.383001540546234e-05 -6.394267892627056e-05 + 1 3 1.298396137438121e-06 1.237633789783706e-06 -9.118263571321790e-07 4.747807185794977e-07 3.463948395611681e-06 3.351712834490921e-06 -2.047246911240814e-06 1.198313829388473e-06 + 1 4 -6.395898892413310e-08 -4.316037619297452e-08 6.306559576159507e-09 3.709208174695814e-09 -1.791364720584111e-07 -1.437298811982069e-07 -5.257381648505548e-08 7.905992894395630e-08 + 1 5 -4.002686750901875e-09 -5.528464462687806e-09 2.834096730591548e-09 4.895910669240819e-09 -1.633079108054013e-08 -2.293209524292142e-08 7.156481707891762e-09 1.069828297878333e-08 + 1 6 3.431826052009797e-10 -3.924528509983503e-09 1.773716230688722e-09 4.043517786933014e-09 -4.753312368659780e-10 -1.015158222606543e-08 3.681556575455224e-09 9.173682576757014e-09 + 1 7 7.760241047880202e-10 -2.109013231610461e-09 1.809838351906849e-09 2.192539857602038e-09 1.714231680739782e-09 -5.185281392897838e-09 4.216544521672336e-09 5.131893684835592e-09 + 1 8 6.350719701243529e-10 -1.240606452839398e-09 1.400428321607383e-09 1.432649386752136e-09 1.404117604594337e-09 -3.114598000041755e-09 3.303760271115836e-09 3.640300306521178e-09 + 2 -8 2.874730225141279e-10 -2.706776381520215e-10 -3.076823298219263e-10 -3.001706022046112e-10 1.101035780833025e-09 -1.069653611243209e-09 -1.147733206147572e-09 -1.095578465603304e-09 + 2 -7 5.008051087007208e-10 -5.522152544292046e-10 -5.212996917596379e-10 -6.422008621672908e-10 1.537157215856869e-09 -1.839958597380168e-09 -1.306200557173264e-09 -1.832606669960485e-09 + 2 -6 8.122167333760539e-10 -8.811535296328687e-10 -8.417117626414038e-10 -1.083405069964192e-09 2.294665706380220e-09 -2.549632409153949e-09 -2.008542894014176e-09 -2.689714171842857e-09 + 2 -5 1.276031727058793e-10 -2.005138763234812e-10 -2.422843855526505e-09 -2.968124663609470e-09 4.060316403880747e-09 -4.815711633833853e-09 -3.038099738221138e-09 -5.177924353409689e-09 + 2 -4 -7.452205099918058e-09 3.282828685521358e-09 1.314622754509926e-08 1.486126328153615e-08 -1.766323145294325e-08 9.867460508483144e-10 -3.381174375264974e-08 -2.405795579416215e-08 + 2 -3 1.537652263615765e-07 -1.529441137917612e-07 3.572625307187020e-08 -3.953764526600967e-08 -3.440769518012183e-07 3.441934429159791e-07 3.445996813270162e-07 4.155156265547598e-09 + 2 -2 4.035693560259536e-07 -3.468004678943036e-08 -2.529271905411641e-06 -2.475802173084159e-06 3.686280480907342e-06 -1.774921041728090e-06 4.673460051241489e-06 5.286802687038037e-06 + 2 -1 -2.122630378900708e-05 2.600484160065195e-05 2.537376424484247e-05 2.603562990659399e-05 2.781973994516649e-05 -1.341308211459195e-05 -5.745039784221623e-05 -5.001925553258801e-05 + 2 0 1.063371261614955e-03 -9.351012120699257e-04 -1.539147562579156e-04 -5.998009307813546e-05 -7.727063925344844e-04 1.179134232382115e-03 -1.137624390967074e-04 1.540407463836563e-04 + 2 1 3.728773510040462e-04 -6.983360214148999e-04 -1.277166535792317e-05 -1.631551378509541e-04 -2.049734998719606e-04 6.993954609714700e-04 9.662607175286411e-05 1.247623746628514e-04 + 2 2 -7.230412517406391e-05 -7.473811579489748e-05 5.569012119484639e-05 -2.501143931861464e-05 1.513313246301526e-04 1.602479841280785e-04 -9.850493024029867e-05 8.504351019461247e-05 + 2 3 3.763176839166248e-07 8.792416879303811e-07 -2.026220558283121e-07 6.429306238927900e-07 -4.863217023237372e-06 -5.355360252279489e-06 -1.076240377098182e-05 1.144478347671509e-05 + 2 4 1.131059208799842e-07 4.505737080545409e-08 -7.225858962595568e-08 8.020962146458769e-08 -3.162342256318263e-07 -3.493488392576747e-07 2.581452258913891e-07 -2.020859822738473e-07 + 2 5 1.117932788087377e-08 4.858867098787012e-09 -6.747544584507884e-09 6.417109269744687e-09 9.028529511193416e-08 7.389074710149153e-08 -2.577693557261565e-09 1.377589935875461e-08 + 2 6 2.301764025922272e-09 6.274546751153798e-10 7.842254853289865e-11 2.856387371857697e-09 1.092758745633040e-08 6.327216115870605e-09 -6.109331595113325e-09 1.394390158100275e-08 + 2 7 3.199388946947777e-10 -5.837141301859883e-10 5.367826693994109e-10 9.716972454235040e-10 8.046387363882840e-10 -1.547202948515446e-09 1.570808509909518e-09 3.148712808505356e-09 + 2 8 2.239847684710401e-10 -4.139309411547270e-10 3.486986874029815e-10 5.678452121180568e-10 5.700577998897687e-10 -1.070682268710484e-09 1.147423604108286e-09 1.850231493476768e-09 + 3 -8 1.777694989958283e-10 -1.791060928721501e-10 1.592738460907335e-11 1.644420309731150e-11 4.208721164874012e-10 -4.296290571694143e-10 1.003300388660581e-10 9.899600060562722e-11 + 3 -7 2.383404343347425e-10 -3.224341054020105e-10 -3.336021521408623e-13 -1.127065295125626e-11 4.365496432621360e-10 -6.396557541365763e-10 2.498140306959090e-10 2.553354074071620e-10 + 3 -6 3.444258194861813e-10 -4.666840895397286e-10 6.981632177485613e-11 3.441674558443489e-11 7.160671719138351e-10 -9.403080654565421e-10 3.163765212554030e-10 4.130018878760263e-10 + 3 -5 5.892124858604753e-10 -8.841926243674918e-10 -1.108984057353716e-09 -1.213876956476806e-09 9.844048763013072e-10 -2.283403374652330e-09 3.974770055867862e-11 -4.282372144150836e-10 + 3 -4 -1.433946726914896e-08 1.412995225803546e-08 9.704740607188350e-09 9.960624962478328e-09 -5.718415906626132e-09 5.976646224710108e-09 3.677376585553809e-10 7.218391477682015e-09 + 3 -3 -5.055876172004847e-08 4.307503292582196e-08 6.215708848901514e-08 4.633937772748124e-08 -6.950616084800681e-08 -3.687559491984598e-08 7.510377611710683e-08 -8.544895352323356e-08 + 3 -2 -1.709979723383758e-07 1.496176527644646e-07 8.298713752517277e-07 7.407077096261949e-07 -2.558080358684755e-07 -7.963344725527261e-08 8.664557931997626e-07 -1.174845769371228e-07 + 3 -1 8.480137439431538e-06 -8.487753464264510e-06 -1.443299779435024e-06 -2.340725775826137e-06 5.411513325549776e-06 -6.487949016935553e-06 1.632778657338694e-06 -7.714055788076447e-06 + 3 0 -3.313255073656245e-05 4.504487377376952e-05 -1.722630827846313e-04 -1.765378831444662e-04 1.074432554805728e-05 9.021685520295440e-05 -2.670168487324044e-04 -3.030029084163711e-04 + 3 1 -5.087121584256535e-03 5.116645881079767e-03 1.326970144368675e-03 1.253871365141916e-03 -1.026509085983511e-02 1.026371228768204e-02 2.669471160062606e-03 2.491439636469530e-03 + 3 2 8.959571749151821e-04 8.622913228587512e-04 -4.285692627785252e-04 5.094882874077612e-04 1.807615217507021e-03 1.755806992704813e-03 -8.265793546009188e-04 1.022998426840788e-03 + 3 3 -2.488286347765871e-05 -2.833334022399263e-05 -4.743834049092280e-05 4.646477398111292e-05 -4.695965106387879e-05 -5.435431802115176e-05 -9.610644168956076e-05 9.295497835574099e-05 + 3 4 -2.433766979822312e-06 -2.347965558918800e-06 1.476547330096468e-06 -1.488401142871561e-06 -4.259745012678309e-06 -3.995359587680057e-06 3.372029098760316e-06 -3.495349353958198e-06 + 3 5 7.543750755590076e-08 8.975952624473694e-08 1.154283607919535e-07 -1.188525749975178e-07 1.969099033211810e-07 2.454060071425630e-07 2.605189612769241e-07 -2.701555457246008e-07 + 3 6 6.639779326957121e-09 9.701216004623292e-09 -3.882681532920610e-09 2.395951736881451e-09 2.068814490979637e-08 2.998317135461603e-08 -5.798154869460117e-09 1.400040655458935e-09 + 3 7 2.247162565803643e-11 4.943386342525483e-10 -1.133261368490032e-10 3.154218006906472e-10 6.588528537632743e-11 1.664878885948544e-09 5.873097525195203e-11 1.044244321863107e-10 + 3 8 3.266230828545093e-11 7.487275720457799e-11 1.502134291795754e-10 2.510295569484051e-10 -7.846676527620357e-12 4.242941702954007e-10 2.296905043987267e-10 3.840700107402091e-10 + 4 -8 9.099677380298720e-12 -7.670213166416014e-12 -4.881641853367531e-12 -5.146999907756678e-12 2.710041699480554e-11 -9.902225290713535e-12 -2.238470799595647e-11 -2.883055125461924e-11 + 4 -7 -1.344761967332013e-11 1.077800947227093e-11 3.894288410210971e-11 3.769624883662832e-11 -7.090863520678464e-11 7.208937980346747e-11 1.360753310088329e-10 1.394975474036972e-10 + 4 -6 -1.921140913104907e-12 2.400843782930785e-12 6.685443462995482e-11 8.258470606699314e-11 9.140125274322566e-12 5.910709211380331e-12 1.376058107839175e-10 1.920353555771325e-10 + 4 -5 5.446030661345722e-11 -7.924863778742208e-11 3.065216933505328e-11 5.184570210033571e-11 -1.287811478435119e-10 1.358208747970191e-12 4.195303281794048e-10 3.882622226938529e-10 + 4 -4 -3.846757586800782e-09 3.822941965912423e-09 -1.274593539470023e-09 -1.270467985952193e-09 -1.401813295591428e-08 1.492619012644721e-08 -6.658927035325521e-09 -5.364315034516646e-09 + 4 -3 -4.191352690379068e-09 5.966834921884161e-09 4.545538934809944e-08 4.445333172467055e-08 -1.087231098066446e-08 1.505751083785851e-08 1.979896470172222e-07 1.980363380263228e-07 + 4 -2 1.413020993177385e-07 -1.423434557817333e-07 1.227809953422816e-07 1.450773354290232e-07 8.761150190953027e-07 -8.457056403843486e-07 1.843472740187274e-07 3.161584423893988e-07 + 4 -1 4.341416040753799e-06 -4.520907498625143e-06 1.734631953100008e-06 1.738349187412195e-06 1.436645008915578e-05 -1.618513247298195e-05 4.733081890999666e-06 4.544048499634805e-06 + 4 0 6.216128072488746e-05 -6.608111136022102e-05 -5.096143996993759e-05 -5.090030143065288e-05 1.968854570544691e-04 -2.323016389606880e-04 -1.517345871040889e-04 -1.551498051652837e-04 + 4 1 -1.126475074090620e-04 8.410114527357613e-05 7.100731242831333e-05 3.581431989162323e-05 -6.393619538512870e-05 -5.029362074848695e-05 1.382575176615545e-04 2.050438204199364e-05 + 4 2 -2.058393378648609e-05 5.513110487586256e-05 6.962996844494708e-06 3.811178369511244e-05 5.809246184394435e-05 -2.685050983087061e-05 -4.915591334450865e-05 5.868516293488592e-06 + 4 3 4.447416156638914e-06 4.133554826617014e-06 -6.393784821734942e-06 5.354019648401766e-06 -1.724779759015569e-05 -1.175566441470858e-05 5.292790613473838e-06 -1.783477435698734e-05 + 4 4 -7.130651399829148e-08 -2.617958938657005e-07 2.373713866934590e-08 -6.458568589097219e-08 2.138939102198504e-06 1.914616755664016e-06 2.470759036616283e-06 -2.269604378372281e-06 + 4 5 1.552495341770712e-09 -2.750088291378323e-08 7.899836293448525e-09 -1.830500099815881e-08 1.511333241224931e-07 -5.240298032596420e-09 -1.392351275354565e-07 1.044085172312984e-07 + 4 6 -1.198447923744087e-09 -5.062893471570325e-09 1.890140282664213e-09 -4.436732388384485e-09 -1.297010378254632e-08 -3.294502655379767e-08 1.840269644941497e-09 -1.632480196795177e-08 + 4 7 -2.814666585048379e-10 -2.688741165199219e-10 6.311653337064517e-10 -8.071650082927736e-10 -1.716748638304981e-09 -2.152875047493353e-09 3.853045956743605e-09 -4.361308738488131e-09 + 4 8 -6.080181062923687e-12 8.210428717342094e-11 -1.829155788842824e-11 -1.094663829756737e-10 7.193104112525001e-11 2.319089421525641e-10 2.545251533138524e-11 -3.465852969939852e-10 + 5 -8 -2.669139540928091e-13 4.698820592785202e-13 -8.660253893105763e-13 -5.431842964557214e-13 -3.898832985219361e-12 1.997439817496616e-12 -5.889636905807223e-12 3.985821929192387e-12 + 5 -7 -1.801208548286219e-11 1.805551476891176e-11 1.382114568156510e-12 1.114627963143384e-12 -4.513270117718036e-11 3.608486460714887e-11 -3.653301660163329e-12 -9.697402544107292e-12 + 5 -6 -2.193294907590522e-11 3.001964640601796e-11 4.118638995322667e-12 5.914233930122962e-12 -2.308214817553468e-11 4.977240220636815e-11 -4.387481221019816e-11 -5.361976883665739e-11 + 5 -5 -6.223873370202321e-11 7.155917022884055e-11 2.996175213314149e-11 3.628149367913277e-11 -2.809000179746720e-10 2.928166180778536e-10 1.766590400910796e-10 1.586354055076726e-10 + 5 -4 -3.936102551146687e-10 3.840576083853817e-10 -6.456105770117249e-10 -6.381822594099647e-10 -2.406056397704874e-09 2.299896704860056e-09 -6.444893490388545e-09 -6.597315625151220e-09 + 5 -3 -7.339390295311443e-09 7.765793675671806e-09 8.923218461699574e-09 8.281773290883027e-09 -7.142888873542924e-08 7.497529611726418e-08 7.377123947163246e-08 6.834213878407007e-08 + 5 -2 -4.800758341747962e-08 5.293822411139114e-08 5.199792282665382e-08 5.784726984486798e-08 -4.123561970398102e-07 4.547296893150378e-07 4.469119333549541e-07 4.973753423643017e-07 + 5 -1 1.759382798754049e-08 2.525405136269196e-08 8.754569385187733e-07 9.266836845474051e-07 -8.466146980491286e-07 1.222910545623143e-06 6.766638035709763e-06 7.194924482862005e-06 + 5 0 7.333556732686594e-06 -7.427588820809712e-06 7.313754721614676e-07 1.460364474632379e-06 5.425661295796407e-05 -5.325617858722969e-05 1.265427750164829e-05 1.803245240949694e-05 + 5 1 5.010923260206821e-06 -9.750771987895155e-06 2.441856340371966e-05 2.377689050493411e-05 -2.419655333032779e-05 -1.017028722666939e-05 7.306095441788181e-05 7.314756621304179e-05 + 5 2 2.442739824273377e-04 -2.492903943997982e-04 -1.340283783860473e-04 -1.244857324199373e-04 5.101038569308111e-04 -5.124903630344561e-04 -2.632264215783870e-04 -2.266875295370781e-04 + 5 3 -4.891585414097481e-05 -3.300921545681005e-05 2.262688094926820e-05 -4.925490096370108e-05 -1.007614308017720e-04 -7.380010276383178e-05 4.379235593354428e-05 -9.285826334256841e-05 + 5 4 3.123039156983536e-06 4.444738552109621e-06 4.546677074537174e-06 -3.545066031305461e-06 5.944300041616924e-06 7.222184858568008e-06 9.251701284925513e-06 -6.363837109955947e-06 + 5 5 3.087287480097356e-07 2.477052531568325e-07 -2.653843896734462e-07 3.482199571942343e-07 3.757833340012013e-07 1.851119825069280e-07 -5.059847464245063e-07 7.702028729623193e-07 + 5 6 -1.934917851139556e-08 -2.667481107640315e-08 -1.999305883626385e-08 1.882990165410042e-08 -4.441630861138737e-08 -7.368665295727156e-08 -2.775889499521497e-08 3.449679104827633e-08 + 5 7 -1.587098319575016e-09 -1.872334458994015e-09 1.349692438735818e-09 -1.387169204411715e-09 -5.060272656702438e-09 -5.776024109288837e-09 3.147865973746203e-09 -1.976812631368815e-09 + 5 8 5.583358365701500e-11 -5.360544422203843e-12 6.562346777222214e-11 -1.113916403979811e-10 9.390314091066755e-11 -1.198604593069260e-10 9.841884302829231e-11 -1.256289714637018e-10 + 6 -8 7.159698421446669e-13 -8.605220887903966e-13 1.596997486270891e-13 1.499228075712332e-13 6.466062700579490e-12 -1.183839393335184e-11 4.524911655785481e-12 1.476242703638775e-12 + 6 -7 -2.135328485977786e-13 -2.837360983170302e-13 4.480108451808441e-14 -1.237434933359558e-13 2.096316462382459e-12 -3.619452052216971e-12 -2.321450185529311e-12 -7.416910828824755e-12 + 6 -6 3.132145684949786e-13 -2.422961272208981e-13 -4.327974626484718e-12 -4.489503186719090e-12 4.920200018830704e-12 -6.020213923128178e-13 -1.261738746636541e-11 -8.629012523408703e-12 + 6 -5 -9.158784959821604e-12 9.088046689920217e-12 -1.550015758010485e-12 -3.121472940953523e-12 -2.673141177817674e-11 1.205741978218008e-11 6.141799747456691e-11 6.531106152281628e-11 + 6 -4 -1.914384717083784e-11 1.329236548545514e-11 -3.151138899287833e-12 -2.742523463546630e-13 4.346440594165293e-10 -5.932845466114468e-10 -7.928856766510231e-10 -7.352168948761655e-10 + 6 -3 5.641980389860269e-10 -5.791728273038771e-10 5.980536187968393e-10 4.751565680412302e-10 1.976658881522078e-09 -2.551903429673480e-09 1.914661956903727e-09 -3.447548475402002e-10 + 6 -2 3.515655632403833e-10 -6.475290296181937e-10 -7.352608318450944e-09 -7.741714361630502e-09 2.679593747801372e-08 -2.810195514094542e-08 -7.074228759106105e-08 -7.957117663766914e-08 + 6 -1 -6.418166585936157e-08 6.895903604650286e-08 -2.610811423345098e-08 -3.498532387286689e-08 -8.995579950390309e-07 9.304788375651144e-07 -3.149400596192336e-07 -4.357174798479014e-07 + 6 0 -1.557795585558193e-06 1.642707228272053e-06 -2.178379701043582e-07 -2.046531314332763e-07 -1.612406838928942e-05 1.718756727559504e-05 7.652862153456316e-07 8.181964675686984e-07 + 6 1 -6.883137752076093e-06 7.789522689572095e-06 9.528345508948365e-06 9.630385811351882e-06 -2.881796828532072e-05 3.911580417222984e-05 3.238259191387741e-05 3.513966148529251e-05 + 6 2 9.949632273576776e-06 -7.511753233830743e-06 -1.038982915716180e-05 -4.994442031198062e-06 2.934810459471068e-05 -1.549768861910624e-05 -3.390214520878502e-05 -1.214577986747784e-05 + 6 3 2.300346051958631e-07 -3.313754312911968e-06 -4.467739024908736e-07 -4.479032840722187e-06 -1.211020488284986e-05 5.492440816493984e-07 1.295444928553493e-05 -2.246232226061086e-06 + 6 4 -1.103136165139960e-07 -8.429143724850011e-09 5.783228210097146e-07 -4.927701659147033e-07 2.821275203014526e-06 8.541439159627405e-07 -5.665276031513345e-07 2.705386029705306e-06 + 6 5 9.190923850234017e-09 2.228885853451759e-08 -1.251805908429025e-08 2.605169793399308e-08 -4.044337200196656e-07 -4.316783840104614e-07 -3.408109082089906e-07 2.677725862109344e-07 + 6 6 -2.136794036241187e-09 8.171780228231113e-10 5.932640364021544e-10 3.141285337662101e-09 -2.285148626898629e-08 -6.031858504500824e-09 4.020993130179712e-08 -2.539620012995041e-08 + 6 7 1.005383481637834e-10 3.696312796355582e-10 -8.634205992955661e-11 8.432543630029130e-10 1.921239642791208e-09 4.028413238799621e-09 4.069407827530441e-10 3.851845587879371e-09 + 6 8 7.648920913853239e-12 -2.334948293684379e-11 -1.025314581725315e-10 1.574280376820545e-10 1.113264867636701e-10 7.414160795004551e-12 -6.036235032821986e-10 7.890200247166661e-10 + 7 -8 8.072063901225780e-14 -5.423538453754258e-14 -1.515275275163093e-13 -2.139007647455973e-13 5.611578953064622e-13 3.130404581599155e-12 -3.211089655855773e-12 -6.426258230910623e-12 + 7 -7 2.467197771112396e-15 9.378861423105534e-15 3.582659160310338e-13 2.818512195372138e-13 -3.012106034236786e-12 6.217532389355310e-12 2.704090358063821e-12 5.424354476018265e-12 + 7 -6 1.725560156315451e-12 -1.784405066972076e-12 -2.092587894574597e-13 -2.165150571463138e-14 4.631295578811580e-12 -1.011188744892614e-11 3.127672229287207e-12 6.625800036443396e-12 + 7 -5 6.386735114404880e-13 -1.482904332914691e-12 -2.151138169125206e-12 -2.387427798833200e-12 -8.451886930076989e-12 7.246357022582610e-12 8.246073443074822e-12 2.278310937319792e-12 + 7 -4 -2.570905935954373e-11 2.514267809839260e-11 9.007975015036698e-12 9.176892949355281e-12 -1.599931778840391e-10 1.644200735201037e-10 1.376674032737270e-10 2.057495137843461e-10 + 7 -3 1.832601282374606e-10 -1.778578203987433e-10 3.532727543595220e-10 3.624164151444287e-10 1.592693547241850e-09 -1.871573320097678e-09 2.737894339357076e-09 2.986572858084550e-09 + 7 -2 2.425077192103625e-09 -2.675619448256065e-09 -7.525388233293310e-10 -5.763096170232003e-10 1.949815987746918e-08 -2.383200071360591e-08 -7.922759290010990e-09 -6.290660232943728e-09 + 7 -1 2.726075076746377e-08 -2.933703421596410e-08 -1.856076463332730e-08 -1.994206007972644e-08 2.124958429820849e-07 -2.462740005737867e-07 -1.290285304282255e-07 -1.522775112847011e-07 + 7 0 -5.632904583366428e-08 4.032570553923383e-08 -2.989795662220503e-07 -3.182380635519236e-07 3.019564859247620e-07 -4.903196597783117e-07 -2.003167932609825e-06 -2.249574904433446e-06 + 7 1 -1.373660287662260e-06 1.434128987320057e-06 4.273888740706760e-07 2.494002241191592e-07 -1.006795470617991e-05 9.870933822512273e-06 1.531638431795546e-06 8.169977836976633e-08 + 7 2 -7.122005423366335e-07 1.384457638685215e-06 -2.590454589846872e-06 -2.220943377069752e-06 2.246168966170586e-06 2.716600497655771e-06 -1.114240856426433e-05 -9.151883407518896e-06 + 7 3 -1.221823461613148e-05 1.284329509753237e-05 1.108027864687774e-05 1.016475255210545e-05 -2.697171368009640e-05 2.792707200524424e-05 2.134675251264458e-05 1.665014779928071e-05 + 7 4 3.075572393307335e-06 8.075446545858487e-07 -1.426899883082164e-06 3.836804088400153e-06 6.335284499697205e-06 2.861969226894860e-06 -2.564557697926165e-06 7.029603038046887e-06 + 7 5 -3.187208435814503e-07 -4.711461470081176e-07 -3.657732686813867e-07 1.733051904929589e-07 -6.397740541679363e-07 -6.311718814208943e-07 -7.592239419772196e-07 3.420822956150132e-07 + 7 6 -2.940449917256603e-08 -1.372623324970334e-08 3.453127055794750e-08 -4.643949762936051e-08 -2.280437163463908e-08 2.072560589414034e-08 6.069751903304861e-08 -7.672107883581729e-08 + 7 7 3.088429479881206e-09 4.412199544843048e-09 2.156259193604130e-09 -1.245840399728216e-09 5.384292122308359e-09 1.032613397081111e-08 2.431945566644280e-10 1.185606926153265e-09 + 7 8 1.993259496164123e-10 2.008554945835573e-10 -2.728050428373631e-10 3.304918422173821e-10 4.243400949419477e-10 4.879132048856139e-10 -5.689422439772297e-10 6.070833107108635e-10 + 8 -8 -8.157676518818663e-14 1.017653176222833e-13 -2.250505477625673e-14 9.718385475259597e-15 -2.574576023346106e-12 2.768290164346279e-12 -2.630741814700850e-13 3.422829688902942e-12 + 8 -7 -1.328232183542354e-13 1.588977265973604e-13 7.850789723964983e-14 9.898003794404495e-14 -5.321969262672291e-13 -1.422794907272397e-12 2.001181065593549e-12 3.256110652079520e-12 + 8 -6 2.835698356428755e-14 3.093741272519447e-14 2.361306836425034e-14 8.616915684919531e-14 2.827360642102881e-12 -1.858003186830016e-12 -2.370681707513467e-12 -3.416212342238121e-12 + 8 -5 -7.860234650560868e-14 5.973662747677390e-14 -2.949693756164745e-15 2.263404811746820e-14 -5.644426029841578e-12 7.682159625406356e-12 -8.250604970968244e-12 -7.479174793331905e-12 + 8 -4 -7.201289495415918e-12 7.289820962540539e-12 -2.179961591435368e-12 -2.393465389492847e-12 -1.256266454448778e-10 1.334285354218545e-10 -3.203105812184868e-11 -3.714827893614781e-11 + 8 -3 -1.950295679348473e-11 2.719889274745178e-11 6.279352879360682e-11 6.271461379432284e-11 -4.008633146884750e-10 5.799121826355525e-10 1.028971454308245e-09 1.072241943406915e-09 + 8 -2 -1.991222752790973e-10 2.152749249041081e-10 3.441579127024456e-10 4.102916373949593e-10 -2.251737402008495e-09 2.925001290941927e-09 5.068868568856430e-09 6.682687701203482e-09 + 8 -1 2.971462053734225e-09 -2.947200557818756e-09 4.435445098293327e-09 4.899744920523467e-09 2.197995768349787e-08 -2.256230819620221e-08 6.224043765622159e-08 7.356856306201924e-08 + 8 0 3.184285228450096e-08 -3.571359817491653e-08 -5.748679020196860e-09 -2.747089011922965e-09 4.122908776382375e-07 -4.786477401586018e-07 -1.463088141943764e-09 5.580426240227131e-08 + 8 1 2.552769515666777e-07 -2.775888080784481e-07 4.946511350812957e-09 -6.412507700580616e-09 2.968618812495961e-06 -3.278382233320496e-06 -5.471126121790955e-07 -6.209925761073740e-07 + 8 2 4.627421664405982e-07 -5.933323082500420e-07 -1.240765890078799e-06 -1.270375044381759e-06 2.711899543500591e-06 -4.226143777025681e-06 -5.204973819888417e-06 -5.804487694017731e-06 + 8 3 -7.817080378780099e-07 6.761578699752286e-07 1.222582544580860e-06 5.899895934159761e-07 -4.187259475774294e-06 3.115214796379499e-06 5.986185521826896e-06 2.820321963394401e-06 + 8 4 1.061043530245115e-07 1.405682666911256e-07 -2.483915986471987e-08 4.313571040198508e-07 1.591633665163278e-06 -9.012547435367010e-08 -2.032213945357463e-06 5.508242131517138e-07 + 8 5 -1.879552925206818e-08 -2.439868801685224e-08 -4.739310141827337e-08 2.857105472606171e-08 -4.272167701060337e-07 -4.638918501540492e-08 6.555152681292947e-08 -3.258760552611915e-07 + 8 6 -3.204749858073748e-10 4.897716597150272e-10 3.212469581485721e-09 -4.141890354112215e-09 5.932961664145535e-08 6.877699679748886e-08 4.529144354917304e-08 -1.937717612133139e-08 + 8 7 4.153866792453256e-10 2.593028807181320e-10 -3.377699612385713e-10 -9.565573954665056e-11 2.493875347568174e-09 7.098923485011453e-10 -7.561407557676655e-09 5.675659583716489e-09 + 8 8 -5.957061653093358e-11 -5.525364406727867e-11 -1.865247141568572e-11 -1.062405144614055e-10 -4.299028791947558e-10 -4.941554633908124e-10 -5.585495017921668e-11 -4.969392042552561e-10 + 9 -8 6.645957392201342e-15 -2.874382156447569e-14 3.776899727544878e-14 4.543884615344217e-14 9.404154306251565e-13 -2.959379152313779e-12 1.899739731072267e-12 1.191270899448973e-12 + 9 -7 1.343821894382552e-14 -2.475257189533872e-14 1.349295463480602e-14 1.767303454061695e-14 7.823038021875159e-13 -1.511794061584066e-12 -1.156333539273618e-12 -2.911027426209155e-12 + 9 -6 -3.695139142803167e-14 3.219159519449252e-14 -8.187411363450233e-14 -7.901742290237766e-14 -2.198121405703133e-12 4.347415053231293e-12 -6.984352433400718e-13 -1.064298661623986e-12 + 9 -5 -1.538315484870312e-13 1.768668113885580e-13 4.703998037238386e-14 2.173926782503972e-14 2.285075992065686e-12 -2.738201341562933e-12 1.983938772055576e-12 4.263200146411232e-12 + 9 -4 -1.211359884139322e-13 1.025090520650628e-13 -4.913324936152389e-13 -5.139031815065144e-13 2.362145535399473e-12 -9.103270446099149e-12 -3.938574625967168e-11 -4.423692834960295e-11 + 9 -3 2.241359135820759e-12 -1.840057337020414e-12 -5.554194260140981e-12 -6.684745671758460e-12 -1.227566445191145e-10 1.328927960155367e-10 -1.520799485566943e-10 -2.062502986867317e-10 + 9 -2 -1.108709129604196e-10 1.144063585573415e-10 -7.581125837737712e-11 -8.219968636371834e-11 -2.273923168837524e-09 2.448670220398621e-09 -1.048164309441880e-09 -1.273861029785746e-09 + 9 -1 -9.094457437095008e-10 1.029967051485336e-09 3.849180186659503e-10 3.613595578755629e-10 -1.727395012356134e-08 2.047747849995752e-08 8.904890067257325e-09 8.215632588116670e-09 + 9 0 -6.425928581935856e-09 7.056645793618139e-09 6.705141252569146e-09 7.292130195153683e-09 -7.383469663551062e-08 9.414804558607687e-08 9.322539407142755e-08 1.067154217911835e-07 + 9 1 2.248088151251976e-08 -1.966601506374001e-08 5.430809261338694e-08 5.945367605857355e-08 -3.763944352851072e-08 8.198742609319860e-08 4.308418002171212e-07 5.190934984707110e-07 + 9 2 1.540881738907945e-07 -1.712629728100319e-07 -1.235586106732877e-07 -9.546998162558993e-08 1.207061511778393e-06 -1.225882250540692e-06 -8.111960463465227e-07 -5.243435010296794e-07 + 9 3 8.997986923999881e-08 -1.545170830088030e-07 2.507071293616312e-07 1.767858693101532e-07 -9.022896070983054e-08 -4.138240421857809e-07 1.458102216198225e-06 9.325916210238820e-07 + 9 4 5.937838003346633e-07 -6.600564489544094e-07 -8.431522446730486e-07 -7.639909087592499e-07 1.457641699054854e-06 -1.613434022041675e-06 -1.623177248209507e-06 -1.072465690785483e-06 + 9 5 -2.072138376859067e-07 3.315931406708673e-08 1.018193472000637e-07 -2.702433758279290e-07 -4.331989697081155e-07 -8.529760521617342e-08 1.560695519576881e-07 -4.997540456262304e-07 + 9 6 2.977716552477684e-08 4.171615732781355e-08 2.738635813334015e-08 -2.496392686633193e-09 6.406104445964064e-08 4.755320313242537e-08 5.686544405460106e-08 -1.625427445207543e-08 + 9 7 2.408894771805426e-09 -3.229010521536771e-11 -3.848003447392700e-09 4.805533648837495e-09 3.142861388138564e-10 -4.688770756300245e-09 -6.676724097092652e-09 5.118909524845958e-09 + 9 8 -4.242791983808744e-10 -5.725837129436598e-10 -1.847516994834737e-10 -1.560132416007833e-11 -4.953587354772186e-10 -8.454078957388048e-10 3.419920885099806e-10 -6.753818016726443e-10 + 10 -8 1.544886117420873e-14 -1.089954785595747e-14 -1.487630527194942e-14 -2.848676561577692e-14 5.267829163127372e-13 4.399092138030744e-13 -1.271291989845375e-12 -2.432074035242944e-12 + 10 -7 8.181154515978545e-15 -6.912266332328893e-15 -2.947243923861246e-15 -7.944931697398427e-15 -4.886395842243438e-13 1.767648543411968e-12 -3.544819216082360e-13 -1.387828979478368e-13 + 10 -6 1.092903172382404e-14 -1.454041926294344e-14 -1.608232990829337e-14 -1.932636737226117e-14 -2.645894478948478e-14 -1.441496713208009e-12 2.665084044360851e-13 1.131792987698176e-12 + 10 -5 -1.470691520538941e-14 1.050522650870941e-14 2.679749431067237e-15 -7.597594327205031e-15 5.588869747306412e-13 -1.289896346597976e-12 5.589986243163118e-13 -1.716628709206437e-12 + 10 -4 2.595729871987385e-14 -2.482847115100683e-14 2.364690612277703e-13 2.539077021603741e-13 4.856541502645481e-12 -2.801282668794016e-12 4.666709610270481e-12 6.135041392090372e-12 + 10 -3 3.866787320798055e-12 -4.042059706370816e-12 3.427265072336085e-13 5.188988481542264e-13 9.636496446325965e-11 -1.091293150183128e-10 -1.121181520680161e-11 -3.531965285184967e-12 + 10 -2 1.322410621346429e-11 -1.669130231922975e-11 -2.480784866937184e-11 -2.578516250887642e-11 4.677772191864018e-10 -5.881282866269906e-10 -6.885646562824077e-10 -7.381409056108407e-10 + 10 -1 2.563191930956053e-11 -3.209934932705050e-11 -2.288165010207548e-10 -2.544517161046184e-10 -5.196679003785474e-11 -3.616032436440601e-10 -5.667012550137859e-09 -6.595697388415431e-09 + 10 0 -1.664956162866457e-09 1.705809269975701e-09 -1.090664952632173e-09 -1.273325096551934e-09 -3.080501846004862e-08 3.191818250286566e-08 -2.080412536330343e-08 -2.644013199522021e-08 + 10 1 -6.218808915103592e-09 7.543409185948747e-09 4.048144511335239e-09 3.572013762507523e-09 -8.799628195370653e-08 1.166919528152073e-07 2.621877872471209e-08 1.514304678394177e-08 + 10 2 -3.191343093399008e-08 3.524467684854207e-08 1.706829251247795e-09 5.204491273748077e-09 -3.558084046396312e-07 4.124430265857929e-07 8.280620537973879e-08 1.146594329744486e-07 + 10 3 -1.192768303812877e-08 2.717041504094864e-08 1.348298521586727e-07 1.396857519279406e-07 -1.596834944364167e-07 3.279831869225101e-07 7.209510341738255e-07 7.910840483194865e-07 + 10 4 5.318121288654208e-08 -5.901082826373209e-08 -1.297868618435976e-07 -6.601741477632229e-08 4.053459851503233e-07 -3.751296573634738e-07 -8.346465163581559e-07 -4.373985138172283e-07 + 10 5 -1.743952183701716e-08 3.966623596405432e-10 1.039181514853428e-08 -3.751008848181433e-08 -1.741089004724236e-07 3.801045484540719e-08 2.547003415735843e-07 -8.669880665201994e-08 + 10 6 4.283503416341348e-09 3.369750436640372e-09 3.443542854030370e-09 -4.818217088780400e-10 5.709092297877517e-08 1.829378694310391e-09 -8.470936008672115e-09 3.548775507430536e-08 + 10 7 -1.172515183301942e-10 -4.060441221626071e-10 -5.610114417001616e-10 3.957535764146194e-10 -7.803389589558419e-09 -8.771836495329159e-09 -5.742513875898595e-09 4.923469901532169e-10 + 10 8 -6.782538064140675e-11 1.242572696306183e-11 1.302097382732650e-10 -8.177555538526860e-11 -2.210990582816217e-10 6.741007268641609e-11 1.107094283743895e-09 -8.942272145616133e-10 + 11 -8 -1.028946604885359e-14 1.962106990629213e-14 -8.834664731978012e-15 -9.668258103646174e-16 -8.450698096769665e-13 1.228455733985704e-12 -1.384960183228104e-13 8.846737212802603e-13 + 11 -7 -3.213918206948082e-15 3.758019799357088e-15 -4.734914949637180e-16 9.335513202407924e-16 -1.829869947335953e-13 -3.436647818556341e-15 6.642581032526250e-13 1.329122954947339e-12 + 11 -6 4.829350432006492e-16 1.515418634068943e-15 1.249525757545034e-16 -4.631081555139422e-16 1.206600194873222e-12 -1.075613259205626e-12 -1.439049126729879e-14 -2.596693975453555e-13 + 11 -5 4.620735958479900e-15 -4.346130154364203e-15 2.359613431683419e-15 2.675107653287152e-15 -5.334149977000536e-13 1.066324363919938e-12 -1.400174026601464e-12 -1.037009822117576e-12 + 11 -4 -5.710628453636384e-14 5.573973787002812e-14 2.861793428549324e-14 2.932917769193666e-14 -3.914922385363240e-12 4.012718832367316e-12 2.663492981369112e-12 3.278939629519079e-12 + 11 -3 9.388714508430358e-14 -4.639607386227845e-14 5.939890260193531e-13 6.312135445801750e-13 1.251506968153272e-12 1.244126728819490e-12 2.428754832810459e-11 2.708286649831066e-11 + 11 -2 1.703053320116058e-12 -1.824145009765504e-12 3.862165065643835e-12 4.434733691426818e-12 5.914716761991593e-11 -6.095802533206408e-11 1.329977651446009e-10 1.682776541349133e-10 + 11 -1 5.176054344736716e-11 -5.470884484042009e-11 8.835826732648379e-12 1.213177093205805e-11 1.455432526902963e-09 -1.600760116006733e-09 4.268025995598114e-10 5.994794615174278e-10 + 11 0 2.181534800329894e-10 -2.613956800261258e-10 -2.007387235583095e-10 -2.072167349712865e-10 8.158781121785977e-09 -9.742759171368459e-09 -4.926897299974606e-09 -4.964397533863890e-09 + 11 1 9.114626414889328e-10 -1.021814701587948e-09 -1.484558399361738e-09 -1.673861145058588e-09 1.353558367594108e-08 -1.962917635281143e-08 -2.635467225692176e-08 -3.159855539314893e-08 + 11 2 -5.212383361676572e-09 4.916152569234782e-09 -7.313402666353683e-09 -8.267684147914095e-09 -1.410603309913236e-08 7.880424695112289e-09 -7.268491270290704e-08 -9.216275852556151e-08 + 11 3 -1.220781826057510e-08 1.558664155836899e-08 2.048274901842141e-08 1.709104751708653e-08 -1.021367975050433e-07 1.198302431563501e-07 1.708972361334600e-07 1.276441381989995e-07 + 11 4 -1.040681886372510e-08 1.465442585683175e-08 -2.344528605302819e-08 -1.288150022117538e-08 -8.038887641482993e-09 4.178111115077605e-08 -1.869954240217108e-07 -9.486177283048781e-08 + 11 5 -2.667768236983746e-08 3.305723328139599e-08 6.129812296933217e-08 5.461344038511035e-08 -8.338491050338180e-08 1.029920977288049e-07 1.275045512262688e-07 5.997333788277689e-08 + 11 6 1.446756047315252e-08 -7.642734228296114e-09 -7.857806313239105e-09 1.778919407606615e-08 3.304578721230144e-08 4.129038770792757e-10 -1.112740198659094e-08 3.529034857148920e-08 + 11 7 -2.640900872179114e-09 -3.316309684002990e-09 -1.980244239949521e-09 -6.993184228901609e-10 -6.355394248495517e-09 -3.450212070574222e-09 -3.914362830192705e-09 5.966354356385917e-10 + 11 8 -1.964083223003637e-10 1.672426375483825e-10 4.682341128102895e-10 -5.205701381772589e-10 2.081192941755067e-10 5.558235847139504e-10 6.754928495821062e-10 -2.052902938517221e-10 + 12 -8 1.092379068170499e-16 -5.102461271166528e-15 1.166316509367752e-14 8.130598222278554e-15 2.316611770224732e-13 -7.955754825437283e-13 6.890259240883769e-13 4.907549168659864e-13 + 12 -7 4.540624974282062e-16 -1.535678489037914e-15 1.238240264891551e-15 3.271447569927586e-16 3.472306858242482e-13 -7.786895688445435e-13 -2.553720627512086e-13 -5.006031202590202e-13 + 12 -6 -1.588339567112527e-15 1.998717981145213e-15 2.793163571583697e-16 7.700187231027943e-16 -8.184821427150468e-13 1.064605993890629e-12 -2.181809938436523e-13 -5.528486987002861e-13 + 12 -5 -1.959847727427726e-16 1.128221296002887e-15 2.213887004643106e-15 2.676884617056304e-15 -2.067182880866850e-14 2.587209603091814e-13 9.816583809700851e-13 1.443074537247692e-12 + 12 -4 7.614397514297105e-16 1.959978617219806e-15 -1.092370119424624e-14 -9.864455414478558e-15 7.061022008280015e-13 -1.608537644873556e-12 -1.125888931336082e-12 -1.285454315560784e-12 + 12 -3 -7.978760365333570e-14 9.456972473567920e-14 -1.035866914679298e-13 -8.799265157739958e-14 -6.193727952195699e-12 7.535965405483275e-12 -4.121790587585987e-12 -6.141828163767011e-12 + 12 -2 -1.572634244403585e-12 1.565218056517859e-12 3.085174710877876e-13 3.942823776141298e-13 -5.915850434066642e-11 6.883380542317813e-11 1.524445312254795e-11 1.643665837760510e-11 + 12 -1 -3.912379759474831e-12 4.515167817336025e-12 9.808961430200604e-12 1.043439739292822e-11 -2.106152099827490e-10 2.686099753432962e-10 3.759816013174261e-10 4.164915655102116e-10 + 12 0 1.487610882341410e-11 -1.373349349688565e-11 7.008202051702033e-11 7.955514415282676e-11 4.340911540009209e-10 -2.810434691938696e-10 2.171625413604578e-09 2.609368754820512e-09 + 12 1 4.508682507628668e-10 -4.754451537538407e-10 1.278356324142007e-10 1.752264892509613e-10 1.008417684113132e-08 -1.056162973680574e-08 2.862758204512754e-09 4.582618615496998e-09 + 12 2 7.052251387790193e-10 -9.968027533135327e-10 -1.033269446822692e-09 -1.012486617229941e-09 1.234382763181695e-08 -1.933672341102642e-08 -1.016864310591520e-08 -9.431883597086025e-09 + 12 3 3.542209233179410e-09 -3.836711240928230e-09 -2.829147140133290e-10 -9.739427647688757e-10 3.616207688262134e-08 -4.309708317764294e-08 -1.980698346472854e-09 -1.060317022969533e-08 + 12 4 -2.279275622528466e-09 6.769536213506160e-10 -1.301291821789414e-08 -1.361476533890771e-08 -3.067711086725851e-09 -1.438301432038085e-08 -8.998358471131711e-08 -9.335983897561073e-08 + 12 5 -2.784353516566697e-09 4.827404890103093e-09 1.284831531887443e-08 7.079753326537938e-09 -2.925990667700169e-08 3.672075303928347e-08 9.899401255660774e-08 5.423710629002679e-08 + 12 6 1.945869261197757e-09 -1.055113364960941e-09 -1.725944449549359e-09 3.015917275153546e-09 1.750212854074719e-08 -8.033641036643083e-09 -2.829887442918312e-08 1.037639033916627e-08 + 12 7 -6.147757997679855e-10 -3.107518653615742e-10 -2.005998673901097e-10 -1.549217407619880e-10 -6.894070589121807e-09 -9.858751937424598e-12 1.152832213491546e-09 -3.728803261803748e-09 + 12 8 1.265152048632761e-10 1.198363484171305e-10 9.749953922184437e-11 4.464771912695922e-11 9.294824432633004e-10 9.542968406307855e-10 6.433742038489872e-10 8.330254779084399e-11 + 13 -8 5.941915557734776e-15 -6.600125348844724e-15 -2.432473167075057e-15 -8.096791799279174e-15 3.088948040092467e-13 -5.361089514323115e-14 -3.532592926259321e-13 -6.832814870117882e-13 + 13 -7 -4.579929606264323e-16 9.645593965699824e-16 -6.285675211136302e-16 -6.099176616901691e-16 -2.453513800550679e-15 3.611432236470862e-13 -1.853999144920627e-13 -2.634134590163917e-13 + 13 -6 4.901176843708571e-16 -1.011252395537297e-15 4.482875529238560e-16 6.778187909296464e-16 -2.480527556228256e-14 -2.520118643310057e-13 1.220728238815877e-13 3.390384128087301e-13 + 13 -5 -2.041274954075677e-16 -5.110714131521019e-16 8.557511863889618e-17 -3.063359003982242e-16 2.851681634225399e-13 -4.610111511311642e-13 2.483693723272079e-14 -4.717133990639581e-13 + 13 -4 6.819795273678655e-16 -1.103482133579997e-15 5.852751041917523e-16 9.321977117380511e-16 3.279453503559181e-13 3.141354964522745e-13 -3.765518543920631e-13 -2.985884358052631e-13 + 13 -3 2.460175497604486e-14 -3.163782843310119e-14 -1.827432000143420e-14 -1.873593734748292e-14 1.164385160894161e-12 -1.961816611926657e-12 -1.450517099921038e-12 -7.798871825079524e-13 + 13 -2 -2.080330884902850e-14 8.271410878184103e-15 -2.394024383455755e-13 -3.103586610819671e-13 6.547836902859316e-13 -2.860466352828818e-12 -1.574502659477138e-11 -1.982340588190799e-11 + 13 -1 -1.195248679029342e-12 1.616438915012981e-12 -1.487280984516188e-12 -1.755028268716334e-12 -6.225747874589125e-11 7.326817709068575e-11 -7.879988053331628e-11 -9.836413542301382e-11 + 13 0 -1.567837854174843e-11 1.796509429152623e-11 2.786497410802320e-12 3.259681577115626e-12 -6.092777617014294e-10 6.952831589219671e-10 1.355355362687062e-11 -1.564602606212603e-11 + 13 1 -2.895569499999445e-11 3.817916310713347e-11 5.676614799604058e-11 6.413391291559335e-11 -1.950589606354981e-09 2.347232424584388e-09 1.624336702589809e-09 1.763372784224582e-09 + 13 2 -9.283094818240572e-11 9.877295695793960e-11 2.364028791570470e-10 2.780008640867071e-10 -1.658674069400300e-09 2.662420745354741e-09 4.813685828472461e-09 6.134946373340419e-09 + 13 3 9.108853576867052e-10 -8.958444537382496e-10 7.962485836488710e-10 9.270491063607974e-10 6.206603612927964e-09 -5.683298604088747e-09 9.801274586618527e-09 1.284884243977966e-08 + 13 4 5.385545339507046e-10 -1.062779770227299e-09 -2.645643072090515e-09 -2.319933084203888e-09 3.810246405351359e-09 -8.481183756836699e-09 -2.679307688165105e-08 -2.171127811253177e-08 + 13 5 1.147241481492646e-09 -1.230819496648312e-09 2.170945754883911e-09 9.244472547780851e-10 3.250273533922722e-09 -3.071157954597484e-09 2.413758249355239e-08 1.107637198073719e-08 + 13 6 1.021033851869679e-09 -1.601649388043395e-09 -4.347234491362949e-09 -3.768105154275293e-09 5.353104757908876e-09 -7.862670148323320e-09 -1.142207286614334e-08 -2.774636019593553e-09 + 13 7 -1.064240014652857e-09 8.526661307429902e-10 6.061698662592648e-10 -1.091784285018790e-09 -2.884267852214222e-09 2.704689111866611e-10 1.135942089402425e-09 -2.626178920124743e-09 + 13 8 3.663443185313954e-10 3.570779784958666e-10 1.906711050558047e-10 2.328251513191551e-10 6.281011178855889e-10 2.560990284899142e-10 2.218781090035581e-10 -5.735308906933632e-12 + 14 -8 1.034294274305517e-16 6.433523663257826e-15 1.231487068253640e-15 1.062311118202462e-15 -3.042682721452879e-13 3.031209693594724e-13 3.102187106918626e-14 1.446583689858997e-13 + 14 -7 -8.723424199867423e-16 -3.270516437977621e-16 -2.156579439880351e-15 3.725230248392189e-15 -2.603204991491677e-14 2.760847191233080e-13 1.412852773297288e-13 2.362669910317242e-13 + 14 -6 -1.350746950256421e-15 -2.071866721124908e-15 7.301972587384830e-16 -1.399567528707799e-15 2.684011447687661e-13 -1.475261742276385e-13 -8.344387947342067e-15 2.139818140941184e-13 + 14 -5 1.359361751043981e-15 9.266718978000598e-16 1.405050758398567e-15 -1.317072868335006e-15 -1.421341046424859e-13 -9.272460903575860e-14 -3.294394134383269e-13 -2.390836987882096e-13 + 14 -4 2.089087788353607e-15 -1.164810914931990e-15 2.853913953410637e-16 5.266009121538929e-16 -4.818320963398330e-13 4.764053033172068e-13 4.031411539875692e-13 1.567889607618533e-13 + 14 -3 1.140898798395606e-14 -7.450824995158706e-15 -8.266883802337010e-15 -6.095080270763665e-15 9.484896932670206e-13 -6.812503947404806e-13 9.468391388088574e-13 1.100929990803375e-12 + 14 -2 -1.536975958160051e-15 -1.345942038639310e-14 -6.278983788953381e-14 -9.689553624669131e-15 4.467363778318961e-12 -4.916651599568247e-12 -4.714007277244825e-13 6.674490829610009e-13 + 14 -1 1.128326932023393e-13 -5.122015139241482e-13 -3.262305431774649e-13 -1.457387586589910e-13 2.504941019587797e-11 -3.285198055094256e-11 -1.694453651372266e-11 -1.286207415954953e-11 + 14 0 -2.460723010367616e-13 -1.832882760914647e-12 -3.176785607893058e-12 -4.126294563550089e-12 5.345145070961818e-11 -9.762904894010550e-11 -1.597568212132268e-10 -1.763742814406020e-10 + 14 1 -9.013471155459961e-12 7.737841181008054e-12 -1.491151878897665e-11 -1.926130061653958e-11 -1.514214204811284e-10 1.077071693652093e-10 -5.709210357207063e-10 -6.802870420088740e-10 + 14 2 -8.459004982681491e-11 9.351116105312513e-11 1.702465285049265e-12 -1.103215534058326e-11 -2.104576893544904e-09 2.237120728763432e-09 -1.378282406231595e-11 -3.893951744090130e-10 + 14 3 -3.156425518270852e-11 8.859340971223453e-11 1.773283239725584e-10 1.881356479488702e-10 -1.008290130988519e-09 2.219168092141341e-09 2.435784510546244e-09 2.596310209032110e-09 + 14 4 -3.777851343374871e-10 3.798746987634377e-10 1.970179898012503e-11 1.346445212060222e-10 -3.923570194538885e-09 4.383084231876902e-09 -1.682048967815908e-09 -1.089904234345965e-11 + 14 5 5.194298122832049e-10 -3.483186010977894e-10 1.149036331422572e-09 1.201176691680804e-09 2.728801345856591e-09 -7.875325964784877e-10 1.031466845362387e-08 9.926741850486566e-09 + 14 6 4.712343073447973e-11 -3.598501099112515e-10 -1.214882666635034e-09 -7.087617590826459e-10 1.413717124468599e-09 -3.238402353507821e-09 -1.040070031358174e-08 -5.951639656535716e-09 + 14 7 -1.973296677286733e-10 1.558083799098818e-10 2.480640953734525e-10 -2.225946869802788e-10 -1.680832499020794e-09 1.221603920539884e-09 2.885533480603681e-09 -9.926234325485567e-10 + 14 8 1.002123079930533e-10 -8.345297573003200e-11 -9.229700353147841e-11 8.037628653302981e-11 7.214380659829189e-10 7.531475316762685e-12 -1.387410213966795e-10 3.596813154659095e-10 + 15 -8 2.505231599060825e-15 2.734080342331588e-15 4.210832047481515e-15 3.014991600022626e-15 1.300303997709365e-13 -1.256590938743755e-13 1.736285299802916e-13 1.735434913351022e-13 + 15 -7 1.942640225476498e-15 7.819277516325967e-16 -1.629586705778149e-15 6.235553302174504e-16 1.607221832557518e-13 -2.070196213811446e-13 -5.289711602160850e-14 -1.599179296467749e-14 + 15 -6 -1.142540339007930e-15 -2.623637635003489e-16 -1.001484404561073e-15 5.713546757942382e-16 -2.082403036224150e-13 1.574269952861465e-13 -1.149447392282954e-13 -1.400243904278059e-13 + 15 -5 -5.765228064523431e-16 -4.322986976284661e-17 7.029477298267464e-16 -2.245807128203267e-16 -2.764590473006736e-14 8.742683514948912e-14 2.982714884378287e-13 2.725381368682690e-13 + 15 -4 2.001016675078458e-16 5.102200526327739e-16 -7.782074917768761e-17 -4.889250968274077e-16 3.685411456613409e-13 -3.698582328385557e-13 -1.424755801120111e-13 -5.867516189257804e-14 + 15 -3 -4.123954835019136e-15 4.812338848952542e-15 5.757968170953485e-17 -3.403919373608436e-16 -5.267791377782850e-13 4.304269212474405e-13 -3.768490668447501e-13 -4.278096305180954e-13 + 15 -2 -6.666148119567306e-15 9.333625310203813e-15 3.736546497572455e-14 4.011285875675202e-14 -1.094945954261687e-12 1.248982267868940e-12 2.098690308942342e-12 2.010886065838906e-12 + 15 -1 2.093609597790656e-13 -2.208870250544979e-13 1.195190666805924e-13 1.454483104921399e-13 5.445489193697982e-12 -5.200018858572045e-12 8.205785777076995e-12 8.776213086732025e-12 + 15 0 9.461158607992480e-13 -1.101866710757208e-12 -3.584970579066392e-13 -3.342403895296068e-13 3.328889991407975e-11 -3.485757085827302e-11 6.416943754331835e-12 8.455332038155741e-12 + 15 1 2.602086028750327e-12 -3.047224468589582e-12 -2.745715316911180e-12 -3.111177043612837e-12 1.195944944354703e-10 -1.286907151940970e-10 -4.754159301297458e-11 -4.722005473174563e-11 + 15 2 1.809679651572357e-13 -7.943744605076680e-13 -1.075057053429268e-11 -1.242235365291697e-11 2.589238350847585e-10 -2.895594948724628e-10 -3.527613343359772e-10 -3.711401637607420e-10 + 15 3 6.571681679555500e-12 -4.881294165554483e-12 -2.837576382286961e-11 -3.207703940121356e-11 1.635491805948442e-10 -2.198751861846919e-10 -6.674611228927427e-10 -7.839977605379211e-10 + 15 4 -1.266535754663330e-10 1.279716800149134e-10 -7.337215997746448e-11 -8.020235148127760e-11 -1.309430166739691e-09 1.307323987449241e-09 -1.112799418250951e-09 -1.332495657472441e-09 + 15 5 1.637805297818446e-11 2.224997072265902e-11 2.806620952707170e-10 2.671845918566532e-10 4.840003928332541e-10 -5.832474317460519e-11 3.307668760801972e-09 3.068617207357393e-09 + 15 6 -1.113532662282874e-10 1.001684079936473e-10 -1.708458827056311e-10 -9.731080003183368e-11 -5.814632699249499e-10 3.134034416277007e-10 -2.593826584575602e-09 -1.767749803787374e-09 + 15 7 -4.413952874734074e-11 7.401966950626230e-11 2.690563095725808e-10 2.506224891773945e-10 -4.237469762239917e-10 6.656534280439514e-10 9.188969476656588e-10 3.726041129866600e-10 + 15 8 2.004940055615591e-10 -2.035265646408773e-10 -5.931268550429606e-11 9.102667299953253e-12 2.006667605814383e-10 -8.530259533550436e-11 -7.646178744738401e-11 1.433833181801917e-10 + 16 -8 8.478962964529418e-15 8.568295994140691e-15 9.649313882647094e-16 -1.356045988804569e-15 1.452067567244547e-13 1.424660806662282e-13 -3.748731292425618e-14 -6.864068476359277e-14 + 16 -7 6.289102013016093e-15 7.473538596395302e-15 -1.486630414065852e-15 8.291269344257022e-16 2.140174405762726e-13 1.880300085092135e-14 -3.816607176717292e-14 -1.082046022099694e-14 + 16 -6 -2.420014423508954e-15 -1.472928571808375e-15 -3.308636207314777e-15 4.894300633971481e-15 1.176160707461745e-14 -8.567190909901492e-14 -1.483013428337749e-13 -1.819515011375333e-14 + 16 -5 -1.659727820265357e-15 -2.858422209302392e-15 2.664841003105368e-15 -1.072059516234286e-15 -1.279086736089932e-13 4.976482266988766e-14 3.434132513109730e-15 -7.140265658935349e-14 + 16 -4 2.307909118645044e-15 1.354799749013526e-15 3.935723922521619e-16 -1.088090730706400e-15 2.888865772979339e-14 4.531636170947510e-14 1.007702405824155e-13 7.295026318712968e-14 + 16 -3 1.886715141057679e-15 -1.672959591006438e-15 -1.497671779238048e-15 -1.571412499584715e-15 9.299183407920547e-14 -8.911353067446020e-14 -3.692491235113382e-14 -2.562223302659898e-14 + 16 -2 -2.731323610737681e-15 6.008571721975847e-15 -2.593161150098698e-14 -2.778063850844412e-14 1.970623477911608e-14 3.053986160536689e-14 -5.502771662310535e-13 -5.649979908037159e-13 + 16 -1 -1.784458420458870e-13 1.813321114419204e-13 -1.011593107639951e-13 -9.282154953652375e-14 -3.436544445286912e-12 3.397874617944665e-12 -2.894899857984549e-12 -2.673581446559611e-12 + 16 0 -8.827581523708272e-13 8.569781697934395e-13 3.516388578975658e-13 3.773118091428458e-13 -2.015780117437911e-11 1.904903839309727e-11 4.558496259068993e-12 4.943412905364193e-12 + 16 1 -6.514935176979930e-13 5.066183645276325e-13 1.343827750385188e-12 1.310402883702640e-12 -2.260473750104799e-11 1.802775047045661e-11 3.154761712203857e-11 2.854485230975698e-11 + 16 2 2.663020577449252e-12 -2.706426456927519e-12 3.938061344231516e-12 3.524691263878232e-12 3.146023602473277e-11 -3.200334249794734e-11 1.235523589709326e-10 1.083675793795432e-10 + 16 3 1.473867184813847e-11 -1.532391944892252e-11 -2.534870795390840e-12 -3.121539578334883e-12 3.299679997941966e-10 -3.237201236055901e-10 -2.906342160040384e-11 -5.505522444442261e-11 + 16 4 -5.263695729477024e-13 -8.991068100154205e-13 -2.785470683389587e-11 -2.668599092251346e-11 1.006581373944903e-10 -5.098591061840549e-11 -4.554781522369803e-10 -4.224619472094764e-10 + 16 5 3.172343149646664e-11 -4.259062338375103e-11 -6.034628128501891e-12 -1.177024403507466e-11 3.638712291982905e-10 -5.454785559869727e-10 2.335488795686364e-10 2.760820514153359e-10 + 16 6 -5.651114358653908e-11 6.616869550667960e-11 -7.914991082059661e-11 -1.123689990692161e-10 -2.325504013083862e-10 5.299897508497867e-10 -7.681751638979127e-10 -1.097741241100646e-09 + 16 7 -4.593003422959468e-12 1.996545757302638e-11 9.277721394751694e-11 9.713641182055801e-11 -2.186631160326478e-10 4.275332945476761e-11 4.552216241053028e-10 1.060143854431083e-09 + 16 8 -1.780566122616153e-11 2.428985925582853e-11 -5.624466099206129e-11 -1.051412340170232e-10 5.520247835663079e-11 -2.269020618449351e-10 2.987973930439188e-11 -1.878641417807124e-10 From 4165ee11547d0fce1aa3835fed750f0e8aac9b63 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 13 Sep 2023 13:00:49 +0200 Subject: [PATCH 44/61] make NormalField attribute Optional None --- src/simsopt/mhd/spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/mhd/spec.py b/src/simsopt/mhd/spec.py index 4b94a6024..f5c025269 100644 --- a/src/simsopt/mhd/spec.py +++ b/src/simsopt/mhd/spec.py @@ -265,7 +265,7 @@ def __init__(self, if si.lfreebound: self.normal_field = NormalField.from_spec(filename) else: - self.normal_field = None + self.normal_field = Optional[NormalField] = None # By default, all dofs owned by SPEC directly, as opposed to # dofs owned by the boundary surface object, are fixed. From 8801119299d684fc71b520bec8596e6086f454e9 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 13 Sep 2023 14:30:25 +0200 Subject: [PATCH 45/61] fix error in reading VNC array from file --- src/simsopt/field/normal_field.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/simsopt/field/normal_field.py b/src/simsopt/field/normal_field.py index f2910ca79..8c14d3398 100644 --- a/src/simsopt/field/normal_field.py +++ b/src/simsopt/field/normal_field.py @@ -101,18 +101,18 @@ def from_spec(cls, filename): if ph['istellsym']: vnc = None else: - vnc = np.asarray(ph['vnc'][1:]) + vnc = np.asarray(ph['vnc']) - nf = cls( - nfp=ph['nfp'], - stellsym=ph['istellsym'], - mpol=ph['Mpol'], + normal_field = cls( + nfp=ph['nfp'], + stellsym=bool(ph['istellsym']), + mpol=ph['Mpol'], ntor=ph['Ntor'], vns=vns, vnc=vnc ) - return nf + return normal_field def get_index_in_dofs(self, m, n, mpol=None, ntor=None, even=False): """ From 99048994bc969542216079500f7d4a2ff6ff8d96 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 13 Sep 2023 14:49:54 +0200 Subject: [PATCH 46/61] fixed error in Optional syntax --- src/simsopt/mhd/spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/mhd/spec.py b/src/simsopt/mhd/spec.py index f5c025269..b5d6cfd80 100644 --- a/src/simsopt/mhd/spec.py +++ b/src/simsopt/mhd/spec.py @@ -265,7 +265,7 @@ def __init__(self, if si.lfreebound: self.normal_field = NormalField.from_spec(filename) else: - self.normal_field = Optional[NormalField] = None + self.normal_field : Optional[NormalField] = None # By default, all dofs owned by SPEC directly, as opposed to # dofs owned by the boundary surface object, are fixed. From fc62405ded2e39ebb8ac7a4b7c5cf457873acce1 Mon Sep 17 00:00:00 2001 From: Chris Smiet Date: Wed, 13 Sep 2023 21:05:26 +0200 Subject: [PATCH 47/61] fixed typo and mistakes in unit test --- tests/mhd/test_spec.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/mhd/test_spec.py b/tests/mhd/test_spec.py index 96c795722..86943376d 100755 --- a/tests/mhd/test_spec.py +++ b/tests/mhd/test_spec.py @@ -81,18 +81,18 @@ def test_init_from_file(self): def test_init_freeboundary_nonstellsym(self): """ Try creating a Spec instance from a freeboundary file that is also - non-stellarator symmetric. + non-stellarator symmetric. Check value of normal field """ - filename = os.path.join(TEST_DIR, 'M16n08.sp') + filename = os.path.join(TEST_DIR, 'M16N08.sp') with ScratchDir("."): s = Spec(filename) places = 7 self.assertAlmostEqual(s.normal_field.get_vns(0, 1), 3.615260745287559e-04, places) - self.assertAlmostEqual(s.normal_field.get_vns(3, -1), 1.714666510000000E-03, places) + self.assertAlmostEqual(s.normal_field.get_vns(3, -1), -1.269776831212886e-04, places) self.assertAlmostEqual(s.normal_field.get_vnc(1, 0), 1.924871538367248e-04, places) self.assertAlmostEqual(s.normal_field.get_vnc(1, -2), 4.070523669489626e-04, places) @@ -110,7 +110,7 @@ def test_init_freeboundary(self): places = 5 self.assertAlmostEqual(s.normal_field.get_vns(0, 1), -1.116910580000000E-04, places) - self.assertAlmostEqual(s.normal_field.get_vns(3, -1), -1.269776831212886e-04, places) + self.assertAlmostEqual(s.normal_field.get_vns(3, -1), 1.714666510000000E-03, places) def test_run(self): """ From 882d7e76c53d184a035c1755dc6e50bb2e467907 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Wed, 13 Sep 2023 20:21:46 -0400 Subject: [PATCH 48/61] Add unit test to minimize strain for circular coil. --- tests/geo/test_strainopt.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/geo/test_strainopt.py b/tests/geo/test_strainopt.py index 917c2d42c..45737fd9a 100644 --- a/tests/geo/test_strainopt.py +++ b/tests/geo/test_strainopt.py @@ -3,10 +3,44 @@ from simsopt.configs.zoo import get_ncsx_data from simsopt.geo.strain_optimization import LPBinormalCurvatureStrainPenalty, LPTorsionalStrainPenalty import numpy as np - +from simsopt.geo.curvexyzfourier import CurveXYZFourier +from scipy.optimize import minimize class StrainOptTesting(unittest.TestCase): + def test_strain_opt(self): + """ + Check that for a circular coil, strains + can be optimized to vanish using rotation + dofs. + """ + for centroid in [True, False]: + quadpoints = np.linspace(0, 1, 10, endpoint=False) + curve = CurveXYZFourier(quadpoints,order=1) + curve.set('xc(1)',1e-4) + curve.set('ys(1)',1e-4) + curve.fix_all() + order = 2 + np.random.seed(1) + dofs = np.random.standard_normal(size=(2*order+1,)) + rotation = FrameRotation(quadpoints, order) + rotation.x = np.random.standard_normal(size=(2*order+1,)) + if centroid: + framedcurve = FramedCurveCentroid(curve, rotation) + else: + framedcurve = FramedCurveFrenet(curve, rotation) + Jt = LPTorsionalStrainPenalty(framedcurve, width=1e-3, p=2, threshold=0) + Jb = LPBinormalCurvatureStrainPenalty(framedcurve, width=1e-3, p=2, threshold=0) + J = Jt+Jb + def fun(dofs): + J.x = dofs + grad = J.dJ() + return J.J(), grad + res = minimize(fun, J.x, jac=True, method='L-BFGS-B', + options={'maxiter': 100, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) + assert Jt.J() < 1e-12 + assert Jb.J() < 1e-12 + def test_torsion(self): for centroid in [True, False]: for order in [None, 1]: From d131aedc431688db08c1010877e5756a8eb98351 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Wed, 13 Sep 2023 20:25:25 -0400 Subject: [PATCH 49/61] Autopep. --- tests/geo/test_strainopt.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/geo/test_strainopt.py b/tests/geo/test_strainopt.py index 45737fd9a..277aae347 100644 --- a/tests/geo/test_strainopt.py +++ b/tests/geo/test_strainopt.py @@ -6,6 +6,7 @@ from simsopt.geo.curvexyzfourier import CurveXYZFourier from scipy.optimize import minimize + class StrainOptTesting(unittest.TestCase): def test_strain_opt(self): @@ -16,9 +17,9 @@ def test_strain_opt(self): """ for centroid in [True, False]: quadpoints = np.linspace(0, 1, 10, endpoint=False) - curve = CurveXYZFourier(quadpoints,order=1) - curve.set('xc(1)',1e-4) - curve.set('ys(1)',1e-4) + curve = CurveXYZFourier(quadpoints, order=1) + curve.set('xc(1)', 1e-4) + curve.set('ys(1)', 1e-4) curve.fix_all() order = 2 np.random.seed(1) @@ -32,12 +33,13 @@ def test_strain_opt(self): Jt = LPTorsionalStrainPenalty(framedcurve, width=1e-3, p=2, threshold=0) Jb = LPBinormalCurvatureStrainPenalty(framedcurve, width=1e-3, p=2, threshold=0) J = Jt+Jb + def fun(dofs): J.x = dofs grad = J.dJ() return J.J(), grad res = minimize(fun, J.x, jac=True, method='L-BFGS-B', - options={'maxiter': 100, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) + options={'maxiter': 100, 'maxcor': 10, 'gtol': 1e-20, 'ftol': 1e-20}, tol=1e-20) assert Jt.J() < 1e-12 assert Jb.J() < 1e-12 From dfedc9b3880d5e3de16d50ba8734bd3e85fbc580 Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 14 Sep 2023 16:32:10 +0200 Subject: [PATCH 50/61] fix linter error --- src/simsopt/mhd/spec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/simsopt/mhd/spec.py b/src/simsopt/mhd/spec.py index b5d6cfd80..c4347275a 100644 --- a/src/simsopt/mhd/spec.py +++ b/src/simsopt/mhd/spec.py @@ -265,7 +265,7 @@ def __init__(self, if si.lfreebound: self.normal_field = NormalField.from_spec(filename) else: - self.normal_field : Optional[NormalField] = None + self.normal_field: Optional[NormalField] = None # By default, all dofs owned by SPEC directly, as opposed to # dofs owned by the boundary surface object, are fixed. From d02fa165a0d8a4845fcf22ca3f91bd1ea5c68bde Mon Sep 17 00:00:00 2001 From: Chris Date: Thu, 14 Sep 2023 17:10:01 +0200 Subject: [PATCH 51/61] fixed linting again (sorry...) --- tests/mhd/test_spec.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/mhd/test_spec.py b/tests/mhd/test_spec.py index 86943376d..5eb8a789f 100755 --- a/tests/mhd/test_spec.py +++ b/tests/mhd/test_spec.py @@ -96,7 +96,6 @@ def test_init_freeboundary_nonstellsym(self): self.assertAlmostEqual(s.normal_field.get_vnc(1, 0), 1.924871538367248e-04, places) self.assertAlmostEqual(s.normal_field.get_vnc(1, -2), 4.070523669489626e-04, places) - def test_init_freeboundary(self): """ Try creating a Spec instance from a freeboundary file. Check value From 8949d854e88166894e7fc82dace4602e8a7b53c7 Mon Sep 17 00:00:00 2001 From: Joaquim Loizu Date: Fri, 15 Sep 2023 10:25:51 +0200 Subject: [PATCH 52/61] added use of axis aspect ratio argument when plotting poincare sections --- src/simsopt/field/tracing.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/simsopt/field/tracing.py b/src/simsopt/field/tracing.py index 9f52bc343..d8fe8c3ba 100644 --- a/src/simsopt/field/tracing.py +++ b/src/simsopt/field/tracing.py @@ -818,6 +818,8 @@ def plot_poincare_data(fieldlines_phi_hits, phis, filename, mark_lost=False, asp nrowcol = ceil(sqrt(len(phis))) plt.figure() fig, axs = plt.subplots(nrowcol, nrowcol, figsize=(8, 5)) + for ax in axs.ravel(): + ax.set_aspect(aspect) color = None for i in range(len(phis)): row = i//nrowcol From b8c08d5dd2c4fc7a946af017a5545c9a61a5ed88 Mon Sep 17 00:00:00 2001 From: Matt Landreman Date: Fri, 15 Sep 2023 15:25:06 -0400 Subject: [PATCH 53/61] Tweaks to first tutorial from Antoine Cerfon --- docs/source/example_vmec_only.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/example_vmec_only.rst b/docs/source/example_vmec_only.rst index b919cc5fa..16a3c4882 100644 --- a/docs/source/example_vmec_only.rst +++ b/docs/source/example_vmec_only.rst @@ -17,12 +17,13 @@ directory For this problem the two independent variables are ``RBC(1,1)`` and ``ZBS(1,1)``, which control the shape of the plasma boundary:: - R(phi) = 1 + 0.1 * cos(theta) + RBC(1,1) * cos(theta - 5 * phi), - Z(phi) = 0.1 * sin(theta) + ZBS(1,1) * sin(theta - 5 * phi). + R(theta, phi) = 1 + 0.1 * cos(theta) + RBC(1,1) * cos(theta - 5 * phi), + Z(theta, phi) = 0.1 * sin(theta) + ZBS(1,1) * sin(theta - 5 * phi). Note that this boundary has five field periods. We consider the vacuum field inside this boundary magnetic surface, i.e. there is no plasma -current or pressure. The objective function is +current or pressure. The toroidal flux enclosed by the boundary +surface is held fixed. The objective function is .. code-block:: From e00a06b99fd88dd525d707c6b63898627a108ccd Mon Sep 17 00:00:00 2001 From: Rogerio Jorge Date: Thu, 21 Sep 2023 00:16:08 +0200 Subject: [PATCH 54/61] extended B_normal to full torus for easier plotting --- src/simsopt/mhd/virtual_casing.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/simsopt/mhd/virtual_casing.py b/src/simsopt/mhd/virtual_casing.py index 6b26baa0d..5f1788561 100644 --- a/src/simsopt/mhd/virtual_casing.py +++ b/src/simsopt/mhd/virtual_casing.py @@ -265,6 +265,7 @@ def from_vmec(cls, vmec, src_nphi, src_ntheta=None, trgt_nphi=None, trgt_ntheta= vc.unit_normal = unit_normal vc.B_external = Bexternal3d vc.B_external_normal = Bexternal_normal + vc.B_external_normal_extended = np.concatenate([np.concatenate((Bexternal_normal,np.flip(Bexternal_normal,axis=0))) for i in range(nfp)]) if filename is not None: if filename == 'auto': @@ -401,6 +402,17 @@ def plot(self, ax=None, show=True): ax.set_title('B_external_normal [Tesla]') fig.colorbar(contours) fig.tight_layout() + + fig, ax = plt.subplots() + contours = ax.contourf(np.linspace(0,1,len(self.trgt_phi)*self.nfp*2), self.trgt_theta,self.B_external_normal_extended.T, 25*self.nfp*2) + ax.set_xlabel(r'$\phi$') + ax.set_ylabel(r'$\theta$') + ax.set_title('B_external_normal_extended [Tesla]') + fig.colorbar(contours) + fig.tight_layout() + if show: + plt.show() + return ax if show: plt.show() return ax From 35e23349401b7cd87e252dcde2252cf1b5e18f41 Mon Sep 17 00:00:00 2001 From: Rogerio Jorge Date: Thu, 21 Sep 2023 00:28:06 +0200 Subject: [PATCH 55/61] ran autopep --- src/simsopt/mhd/virtual_casing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/simsopt/mhd/virtual_casing.py b/src/simsopt/mhd/virtual_casing.py index 5f1788561..103de3690 100644 --- a/src/simsopt/mhd/virtual_casing.py +++ b/src/simsopt/mhd/virtual_casing.py @@ -265,7 +265,7 @@ def from_vmec(cls, vmec, src_nphi, src_ntheta=None, trgt_nphi=None, trgt_ntheta= vc.unit_normal = unit_normal vc.B_external = Bexternal3d vc.B_external_normal = Bexternal_normal - vc.B_external_normal_extended = np.concatenate([np.concatenate((Bexternal_normal,np.flip(Bexternal_normal,axis=0))) for i in range(nfp)]) + vc.B_external_normal_extended = np.concatenate([np.concatenate((Bexternal_normal, np.flip(Bexternal_normal, axis=0))) for i in range(nfp)]) if filename is not None: if filename == 'auto': @@ -404,7 +404,7 @@ def plot(self, ax=None, show=True): fig.tight_layout() fig, ax = plt.subplots() - contours = ax.contourf(np.linspace(0,1,len(self.trgt_phi)*self.nfp*2), self.trgt_theta,self.B_external_normal_extended.T, 25*self.nfp*2) + contours = ax.contourf(np.linspace(0, 1, len(self.trgt_phi)*self.nfp*2), self.trgt_theta, self.B_external_normal_extended.T, 25*self.nfp*2) ax.set_xlabel(r'$\phi$') ax.set_ylabel(r'$\theta$') ax.set_title('B_external_normal_extended [Tesla]') From 70d69774cbac1d2de7fa0ae4625654f607d52de1 Mon Sep 17 00:00:00 2001 From: Rogerio Jorge Date: Thu, 21 Sep 2023 03:12:35 +0200 Subject: [PATCH 56/61] It's now working as intended --- src/simsopt/mhd/virtual_casing.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/simsopt/mhd/virtual_casing.py b/src/simsopt/mhd/virtual_casing.py index 103de3690..f9e7af99c 100644 --- a/src/simsopt/mhd/virtual_casing.py +++ b/src/simsopt/mhd/virtual_casing.py @@ -265,7 +265,11 @@ def from_vmec(cls, vmec, src_nphi, src_ntheta=None, trgt_nphi=None, trgt_ntheta= vc.unit_normal = unit_normal vc.B_external = Bexternal3d vc.B_external_normal = Bexternal_normal - vc.B_external_normal_extended = np.concatenate([np.concatenate((Bexternal_normal, np.flip(Bexternal_normal, axis=0))) for i in range(nfp)]) + + Bexternal_normal_with_last_point = np.hstack((Bexternal_normal, Bexternal_normal[:, [0]])) + Bexternal_normal_with_last_point = np.vstack((Bexternal_normal_with_last_point, -np.flip(np.flip(Bexternal_normal_with_last_point, axis=0), axis=1)[0])) + flipped_B = -np.flip(np.flip(Bexternal_normal_with_last_point, axis=0), axis=1) + vc.B_external_normal_extended = np.concatenate([np.concatenate((Bexternal_normal, flipped_B[:-1,:-1])) for i in range(nfp)]) if filename is not None: if filename == 'auto': @@ -404,7 +408,7 @@ def plot(self, ax=None, show=True): fig.tight_layout() fig, ax = plt.subplots() - contours = ax.contourf(np.linspace(0, 1, len(self.trgt_phi)*self.nfp*2), self.trgt_theta, self.B_external_normal_extended.T, 25*self.nfp*2) + contours = ax.contourf(np.linspace(0, 1, self.B_external_normal_extended.T.shape[1]), np.linspace(0, 1, self.B_external_normal_extended.T.shape[0]), self.B_external_normal_extended.T, 25*self.nfp*2) ax.set_xlabel(r'$\phi$') ax.set_ylabel(r'$\theta$') ax.set_title('B_external_normal_extended [Tesla]') From 65ce77b3306e6205163c040a6703ede7b6a2dbe7 Mon Sep 17 00:00:00 2001 From: Rogerio Jorge Date: Thu, 21 Sep 2023 16:31:03 +0200 Subject: [PATCH 57/61] removed extra plt.show --- src/simsopt/mhd/virtual_casing.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/simsopt/mhd/virtual_casing.py b/src/simsopt/mhd/virtual_casing.py index f9e7af99c..d953c8c30 100644 --- a/src/simsopt/mhd/virtual_casing.py +++ b/src/simsopt/mhd/virtual_casing.py @@ -417,6 +417,3 @@ def plot(self, ax=None, show=True): if show: plt.show() return ax - if show: - plt.show() - return ax From 96910db450047afd82562a4768b59159ae0f24ec Mon Sep 17 00:00:00 2001 From: Rogerio Jorge Date: Thu, 21 Sep 2023 17:07:35 +0200 Subject: [PATCH 58/61] ran autopep --- src/simsopt/mhd/virtual_casing.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/simsopt/mhd/virtual_casing.py b/src/simsopt/mhd/virtual_casing.py index d953c8c30..8a44b6e19 100644 --- a/src/simsopt/mhd/virtual_casing.py +++ b/src/simsopt/mhd/virtual_casing.py @@ -87,6 +87,9 @@ class VirtualCasing: - ``B_external_normal``: An array of size ``(trgt_nphi, trgt_ntheta)`` with the contribution to the magnetic field due to current outside the surface, taking just the component normal to the surface. + - r``B_external_normal_extended``: An array of size ``(trgt_nphi * nfp * 2, trgt_ntheta)`` with the contribution + to the magnetic field due to current outside the surface, taking just the component + normal to the surface. This is an extension of the B_external_normal array to cover the full torus. The :math:`\phi` and :math:`\theta` grids for these data are both uniformly spaced, and are the same as for @@ -269,7 +272,7 @@ def from_vmec(cls, vmec, src_nphi, src_ntheta=None, trgt_nphi=None, trgt_ntheta= Bexternal_normal_with_last_point = np.hstack((Bexternal_normal, Bexternal_normal[:, [0]])) Bexternal_normal_with_last_point = np.vstack((Bexternal_normal_with_last_point, -np.flip(np.flip(Bexternal_normal_with_last_point, axis=0), axis=1)[0])) flipped_B = -np.flip(np.flip(Bexternal_normal_with_last_point, axis=0), axis=1) - vc.B_external_normal_extended = np.concatenate([np.concatenate((Bexternal_normal, flipped_B[:-1,:-1])) for i in range(nfp)]) + vc.B_external_normal_extended = np.concatenate([np.concatenate((Bexternal_normal, flipped_B[:-1, :-1])) for i in range(nfp)]) if filename is not None: if filename == 'auto': From 8e65c49721003bb43140b153c13a1d78d27bad8b Mon Sep 17 00:00:00 2001 From: Matt Landreman Date: Fri, 22 Sep 2023 07:50:32 +0200 Subject: [PATCH 59/61] Virtual casing: fix test, save B_external_normal_extended --- src/simsopt/mhd/virtual_casing.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/simsopt/mhd/virtual_casing.py b/src/simsopt/mhd/virtual_casing.py index 8a44b6e19..a6ef5a926 100644 --- a/src/simsopt/mhd/virtual_casing.py +++ b/src/simsopt/mhd/virtual_casing.py @@ -296,6 +296,7 @@ def save(self, filename="vcasing.nc"): f.createDimension('src_nphi', self.src_nphi) f.createDimension('trgt_ntheta', self.trgt_ntheta) f.createDimension('trgt_nphi', self.trgt_nphi) + f.createDimension('trgt_nphi_extended', self.trgt_nphi * 2 * self.nfp) f.createDimension('xyz', 3) src_ntheta = f.createVariable('src_ntheta', 'i', tuple()) @@ -368,6 +369,11 @@ def save(self, filename="vcasing.nc"): B_external_normal.description = 'Component of B_external normal to the surface' B_external_normal.units = 'Tesla' + B_external_normal_extended = f.createVariable('B_external_normal_extended', 'd', ('trgt_nphi_extended', 'trgt_ntheta')) + B_external_normal_extended[:, :] = self.B_external_normal_extended + B_external_normal_extended.description = 'Component of B_external normal to the surface, repeated to cover the full torus' + B_external_normal_extended.units = 'Tesla' + @classmethod def load(cls, filename): """ @@ -410,13 +416,20 @@ def plot(self, ax=None, show=True): fig.colorbar(contours) fig.tight_layout() - fig, ax = plt.subplots() - contours = ax.contourf(np.linspace(0, 1, self.B_external_normal_extended.T.shape[1]), np.linspace(0, 1, self.B_external_normal_extended.T.shape[0]), self.B_external_normal_extended.T, 25*self.nfp*2) - ax.set_xlabel(r'$\phi$') - ax.set_ylabel(r'$\theta$') - ax.set_title('B_external_normal_extended [Tesla]') - fig.colorbar(contours) - fig.tight_layout() + fig1, ax1 = plt.subplots() + shape = self.B_external_normal_extended.T.shape + contours = ax1.contourf( + np.linspace(0, 1, shape[1]), + np.linspace(0, 1, shape[0]), + self.B_external_normal_extended.T, + 25, + ) + ax1.set_xlabel(r'$\phi$') + ax1.set_ylabel(r'$\theta$') + ax1.set_title('B_external_normal_extended [Tesla]') + fig1.colorbar(contours) + fig1.tight_layout() + if show: plt.show() return ax From 60329b38e10b2d0547fd20f39302feb341bf8bfc Mon Sep 17 00:00:00 2001 From: Matt Landreman Date: Sat, 30 Sep 2023 19:29:26 -0400 Subject: [PATCH 60/61] Tidy up docs for strain optimization. Renamed StrainOpt -> CoilStrain. --- .../2_Intermediate/strain_optimization.py | 4 +- src/simsopt/geo/__init__.py | 1 - src/simsopt/geo/finitebuild.py | 10 +---- src/simsopt/geo/framedcurve.py | 10 +---- src/simsopt/geo/strain_optimization.py | 42 +++++++++++++------ tests/geo/test_strainopt.py | 2 +- 6 files changed, 35 insertions(+), 34 deletions(-) diff --git a/examples/2_Intermediate/strain_optimization.py b/examples/2_Intermediate/strain_optimization.py index 5087d4fd4..cb04ba2b7 100755 --- a/examples/2_Intermediate/strain_optimization.py +++ b/examples/2_Intermediate/strain_optimization.py @@ -13,7 +13,7 @@ import numpy as np from scipy.optimize import minimize -from simsopt.geo import StrainOpt, LPTorsionalStrainPenalty, LPBinormalCurvatureStrainPenalty +from simsopt.geo import CoilStrain, LPTorsionalStrainPenalty, LPBinormalCurvatureStrainPenalty from simsopt.geo import FrameRotation, FramedCurveFrenet, CurveXYZFourier from simsopt.configs import get_hsx_data from simsopt.util import in_github_actions @@ -40,7 +40,7 @@ Jbin = LPBinormalCurvatureStrainPenalty( framedcurve, p=2, threshold=cur_threshold) -strain = StrainOpt(framedcurve, width) +strain = CoilStrain(framedcurve, width) JF = Jtor + Jbin diff --git a/src/simsopt/geo/__init__.py b/src/simsopt/geo/__init__.py index 98d1a8129..bc6d0efe2 100644 --- a/src/simsopt/geo/__init__.py +++ b/src/simsopt/geo/__init__.py @@ -22,7 +22,6 @@ from .surfacexyzfourier import * from .surfacexyztensorfourier import * from .strain_optimization import * -from .framedcurve import * from .permanent_magnet_grid import * diff --git a/src/simsopt/geo/finitebuild.py b/src/simsopt/geo/finitebuild.py index 3d1783e9d..87a4cc60b 100644 --- a/src/simsopt/geo/finitebuild.py +++ b/src/simsopt/geo/finitebuild.py @@ -1,12 +1,5 @@ import numpy as np -import jax.numpy as jnp -from jax import vjp, jvp - -import simsoptpp as sopp -from .._core.optimizable import Optimizable -from .._core.derivative import Derivative -from .curve import Curve -from .jit import jit + from .framedcurve import FramedCurve, FrameRotation, ZeroRotation, FramedCurveCentroid, FramedCurveFrenet """ @@ -50,7 +43,6 @@ def recompute_bell(self, parent=None): def gamma_impl(self, gamma, quadpoints): assert quadpoints.shape[0] == self.curve.quadpoints.shape[0] assert np.linalg.norm(quadpoints - self.curve.quadpoints) < 1e-15 - c = self.curve t, n, b = self.framedcurve.rotated_frame() gamma[:] = self.curve.gamma() + self.dn * n + self.db * b diff --git a/src/simsopt/geo/framedcurve.py b/src/simsopt/geo/framedcurve.py index d580d5e4b..dc883418f 100644 --- a/src/simsopt/geo/framedcurve.py +++ b/src/simsopt/geo/framedcurve.py @@ -261,7 +261,6 @@ def frame_torsion(self): gamma = self.curve.gamma() d1gamma = self.curve.gammadash() d2gamma = self.curve.gammadashdash() - d3gamma = self.curve.gammadashdashdash() alpha = self.rotation.alpha(self.curve.quadpoints) alphadash = self.rotation.alphadash(self.curve.quadpoints) return self.torsion(gamma, d1gamma, d2gamma, alpha, alphadash) @@ -270,7 +269,6 @@ def frame_binormal_curvature(self): gamma = self.curve.gamma() d1gamma = self.curve.gammadash() d2gamma = self.curve.gammadashdash() - d3gamma = self.curve.gammadashdashdash() alpha = self.rotation.alpha(self.curve.quadpoints) alphadash = self.rotation.alphadash(self.curve.quadpoints) return self.binorm(gamma, d1gamma, d2gamma, alpha, alphadash) @@ -422,7 +420,7 @@ def __init__(self, quadpoints): .. code-block:: python - rot = FilamentRotation(...) + rot = FrameRotation(...) rot.fix_all() """ @@ -604,12 +602,6 @@ def inner(a, b): return np.sum(a*b, axis=1) -torsion2vjp0 = jit(lambda ndash, b, v: vjp( - lambda nd: torsion_pure(nd, b), ndash)[1](v)[0]) -torsion2vjp1 = jit(lambda ndash, b, v: vjp( - lambda bi: torsion_pure(ndash, bi), b)[1](v)[0]) - - def torsion_pure_frenet(gamma, gammadash, gammadashdash, gammadashdashdash, alpha, alphadash): """Torsion function for export/evaulate coil sets""" diff --git a/src/simsopt/geo/strain_optimization.py b/src/simsopt/geo/strain_optimization.py index cbe7ac5d9..59400c107 100644 --- a/src/simsopt/geo/strain_optimization.py +++ b/src/simsopt/geo/strain_optimization.py @@ -6,7 +6,7 @@ from simsopt.geo.curveobjectives import Lp_torsion_pure __all__ = ['LPBinormalCurvatureStrainPenalty', - 'LPTorsionalStrainPenalty', 'StrainOpt'] + 'LPTorsionalStrainPenalty', 'CoilStrain'] class LPBinormalCurvatureStrainPenalty(Optimizable): @@ -15,14 +15,21 @@ class LPBinormalCurvatureStrainPenalty(Optimizable): of the binormal curvature strain, and penalizes where the local strain exceeds a threshold .. math:: - J = \frac{1}{p} \int_{\text{curve}} \text{max}(\epsilon_{\text{bend}} - \epsilon_0, 0)^p ~dl + J = \frac{1}{p} \int_{\text{curve}} \text{max}(\epsilon_{\text{bend}} - \epsilon_0, 0)^p ~dl, - where :math:`\epsilon_0` is a threshold strain, given by the argument ``threshold``. + where + + .. math:: + \epsilon_{\text{bend}} = \frac{w |\hat{\textbf{b}} \cdot \boldsymbol{\kappa}|}{2}, + + :math:`w` is the width of the tape, :math:`\hat{\textbf{b}}` is the + frame binormal vector, :math:`\boldsymbol{\kappa}` is the curvature vector of the + filamentary coil, and :math:`\epsilon_0` is a threshold strain, given by the argument ``threshold``. """ def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.framedcurve = framedcurve - self.strain = StrainOpt(framedcurve, width) + self.strain = CoilStrain(framedcurve, width) self.width = width self.p = p self.threshold = threshold @@ -65,12 +72,18 @@ class LPTorsionalStrainPenalty(Optimizable): .. math:: J = \frac{1}{p} \int_{\text{curve}} \text{max}(\epsilon_{\text{tor}} - \epsilon_0, 0)^p ~dl - where :math:`\epsilon_0` is a threshold strain, given by the argument ``threshold``. + where + + .. math:: + \epsilon_{\text{tor}} = \frac{\tau^2 w^2}{12}, + + :math:`\tau` is the torsion of the tape frame, :math:`w` is the width of the tape, + and :math:`\epsilon_0` is a threshold strain, given by the argument ``threshold``. """ def __init__(self, framedcurve, width=1e-3, p=2, threshold=0): self.framedcurve = framedcurve - self.strain = StrainOpt(framedcurve, width) + self.strain = CoilStrain(framedcurve, width) self.width = width self.p = p self.threshold = threshold @@ -105,7 +118,7 @@ def dJ(self): return_fn_map = {'J': J, 'dJ': dJ} -class StrainOpt(Optimizable): +class CoilStrain(Optimizable): r""" This class evaluates the torsional and binormal curvature strains on HTS, based on a filamentary model of the coil and the orientation of the HTS tape. @@ -119,13 +132,18 @@ class StrainOpt(Optimizable): the expressions for the strains are: .. math:: - \epsilon_{\text{tor}} = \frac{\tau^2 w^2}{12} + \epsilon_{\text{tor}} = \frac{\tau^2 w^2}{12} + \epsilon_{\text{bend}} = \frac{w |\hat{\textbf{b}} \cdot \boldsymbol{\kappa}|}{2}, where :math:`\tau` is the torsion of the tape frame, :math:`\hat{\textbf{b}}` is the - frame binormal vector, and :math:`\boldsymbol{\kappa}` is the curvature vector of the - filamentary coil. + frame binormal vector, :math:`\boldsymbol{\kappa}` is the curvature vector of the + filamentary coil, and :math:`w` is the width of the tape. + This class is not intended to be used as an objective function inside + optimization. For that purpose you should instead use + :obj:`LPBinormalCurvatureStrainPenalty` or :obj:`LPTorsionalStrainPenalty`. + Those classes also compute gradients whereas this class does not. """ def __init__(self, framedcurve, width=1e-3): @@ -143,14 +161,14 @@ def __init__(self, framedcurve, width=1e-3): super().__init__(depends_on=[framedcurve]) def torsional_strain(self): - """ + r""" Returns the value of the torsional strain, :math:`\epsilon_{\text{tor}}`, along the quadpoints defining the filamentary coil. """ return self.torstrain_jax(self.framedcurve.frame_torsion(), self.width) def binormal_curvature_strain(self): - """ + r""" Returns the value of the torsional strain, :math:`\epsilon_{\text{bend}}`, along the quadpoints defining the filamentary coil. """ diff --git a/tests/geo/test_strainopt.py b/tests/geo/test_strainopt.py index 277aae347..87069508e 100644 --- a/tests/geo/test_strainopt.py +++ b/tests/geo/test_strainopt.py @@ -7,7 +7,7 @@ from scipy.optimize import minimize -class StrainOptTesting(unittest.TestCase): +class CoilStrainTesting(unittest.TestCase): def test_strain_opt(self): """ From 51b37b42006e0159660c4684745ed63ac39f1e8c Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Fri, 6 Oct 2023 12:53:27 -0400 Subject: [PATCH 61/61] Initial commit. --- src/simsopt/field/tracing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/simsopt/field/tracing.py b/src/simsopt/field/tracing.py index d8fe8c3ba..f5ab3d164 100644 --- a/src/simsopt/field/tracing.py +++ b/src/simsopt/field/tracing.py @@ -799,7 +799,8 @@ class IterationStoppingCriterion(sopp.IterationStoppingCriterion): pass -def plot_poincare_data(fieldlines_phi_hits, phis, filename, mark_lost=False, aspect='equal', dpi=300, xlims=None, ylims=None, surf=None): +def plot_poincare_data(fieldlines_phi_hits, phis, filename, mark_lost=False, aspect='equal', dpi=300, xlims=None, + ylims=None, surf=None, s=2, marker='o'): """ Create a poincare plot. Usage: @@ -846,7 +847,7 @@ def plot_poincare_data(fieldlines_phi_hits, phis, filename, mark_lost=False, asp if data_this_phi.size == 0: continue r = np.sqrt(data_this_phi[:, 2]**2+data_this_phi[:, 3]**2) - axs[row, col].scatter(r, data_this_phi[:, 4], marker='o', s=2, linewidths=0, c=color) + axs[row, col].scatter(r, data_this_phi[:, 4], marker=marker, s=s, linewidths=0, c=color) plt.rc('axes', axisbelow=True) axs[row, col].grid(True, linewidth=0.5)