rssn 0.2.9

A comprehensive scientific computing library for Rust, aiming for feature parity with NumPy and SymPy.
Documentation
//! # Symbolic Vector Calculus Operations
//!
//! This module provides functions for performing symbolic vector calculus operations,
//! including line integrals (scalar and vector fields), surface integrals (flux),
//! and volume integrals. It defines structures for `ParametricCurve`, `ParametricSurface`,
//! and `Volume` to represent the domains of integration.

use crate::symbolic::calculus::definite_integrate;
use crate::symbolic::calculus::substitute;
use crate::symbolic::core::Expr;
use crate::symbolic::simplify_dag::simplify;
use crate::symbolic::vector::Vector;
use crate::symbolic::vector::partial_derivative_vector;

/// Represents a parameterized curve C given by r(t).
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ParametricCurve {
    /// The vector expression for the curve, e.g., [cos(t), sin(t), t].
    pub r: Vector,
    /// The name of the parameter, e.g., "t".
    pub t_var: String,
    /// The integration bounds for the parameter, e.g., (0, 2*pi).
    pub t_bounds: (Expr, Expr),
}

/// Represents a parameterized surface S given by r(u, v).
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ParametricSurface {
    /// The vector expression for the surface, e.g., [u*cos(v), u*sin(v), v].
    pub r: Vector,
    /// The name of the first parameter, e.g., "u".
    pub u_var: String,
    /// The integration bounds for the first parameter, e.g., (0, 1).
    pub u_bounds: (Expr, Expr),
    /// The name of the second parameter, e.g., "v".
    pub v_var: String,
    /// The integration bounds for the second parameter, e.g., (0, 2*pi).
    pub v_bounds: (Expr, Expr),
}

/// Represents a volume V for triple integration.
/// Defines the integration order as dz dy dx.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Volume {
    /// The bounds for the innermost integral (dz). Can be expressions in terms of x and y.
    pub z_bounds: (Expr, Expr),
    /// The bounds for the middle integral (dy). Can be expressions in terms of x.
    pub y_bounds: (Expr, Expr),
    /// The bounds for the outermost integral (dx). Must be constants.
    pub x_bounds: (Expr, Expr),
    /// The variable names for (x, y, z).
    pub vars: (String, String, String),
}

/// Computes the line integral of a scalar field `f` along a parameterized curve C.
///
/// The integral is `∫_C f ds = ∫_a^b f(r(t)) ||r'(t)|| dt`.
///
/// # Arguments
/// * `scalar_field` - The scalar field `f` as an `Expr`.
/// * `curve` - The `ParametricCurve` representing the path of integration.
///
/// # Returns
/// An `Expr` representing the symbolic line integral.
#[must_use]
pub fn line_integral_scalar(
    scalar_field: &Expr,
    curve: &ParametricCurve,
) -> Expr {
    let r_prime = partial_derivative_vector(&curve.r, &curve.t_var);

    let r_prime_magnitude = r_prime.magnitude();

    let sub = |expr: &Expr| {
        let e1 = substitute(expr, "x", &curve.r.x);

        let e2 = substitute(&e1, "y", &curve.r.y);

        substitute(&e2, "z", &curve.r.z)
    };

    let field_on_curve = sub(scalar_field);

    let integrand = simplify(&Expr::new_mul(field_on_curve, r_prime_magnitude));

    let integral = definite_integrate(
        &integrand,
        &curve.t_var,
        &curve.t_bounds.0,
        &curve.t_bounds.1,
    );

    simplify(&integral)
}

/// Computes the line integral of a vector field `F` along a parameterized curve C (work).
///
/// The integral is `∫_C F · dr = ∫_a^b F(r(t)) · r'(t) dt`.
///
/// # Arguments
/// * `vector_field` - The vector field `F` as a `Vector`.
/// * `curve` - The `ParametricCurve` representing the path of integration.
///
/// # Returns
/// An `Expr` representing the symbolic line integral.
#[must_use]
pub fn line_integral_vector(
    vector_field: &Vector,
    curve: &ParametricCurve,
) -> Expr {
    let r_prime = partial_derivative_vector(&curve.r, &curve.t_var);

    let sub = |expr: &Expr| {
        let e1 = substitute(expr, "x", &curve.r.x);

        let e2 = substitute(&e1, "y", &curve.r.y);

        substitute(&e2, "z", &curve.r.z)
    };

    let field_on_curve = Vector::new(
        sub(&vector_field.x),
        sub(&vector_field.y),
        sub(&vector_field.z),
    );

    let integrand = field_on_curve.dot(&r_prime);

    let integral = definite_integrate(
        &integrand,
        &curve.t_var,
        &curve.t_bounds.0,
        &curve.t_bounds.1,
    );

    simplify(&integral)
}

/// Computes the surface integral (flux) of a vector field F over a parameterized surface S.
///
/// The integral is `∫∫_S (F · dS) = ∫∫_D F(r(u,v)) · (r_u × r_v) du dv`.
///
/// # Arguments
/// * `field` - The vector field `F` as a `Vector`.
/// * `surface` - The `ParametricSurface` representing the surface of integration.
///
/// # Returns
/// An `Expr` representing the symbolic surface integral.
#[must_use]
pub fn surface_integral(
    field: &Vector,
    surface: &ParametricSurface,
) -> Expr {
    let r_u = partial_derivative_vector(&surface.r, &surface.u_var);

    let r_v = partial_derivative_vector(&surface.r, &surface.v_var);

    let normal_vector = r_u.cross(&r_v);

    let sub = |expr: &Expr| {
        let e1 = substitute(expr, "x", &surface.r.x);

        let e2 = substitute(&e1, "y", &surface.r.y);

        substitute(&e2, "z", &surface.r.z)
    };

    let field_on_surface = Vector::new(sub(&field.x), sub(&field.y), sub(&field.z));

    let integrand = field_on_surface.dot(&normal_vector);

    let inner_integral = definite_integrate(
        &integrand,
        &surface.u_var,
        &surface.u_bounds.0,
        &surface.u_bounds.1,
    );

    let outer_integral = definite_integrate(
        &inner_integral,
        &surface.v_var,
        &surface.v_bounds.0,
        &surface.v_bounds.1,
    );

    simplify(&outer_integral)
}

/// Computes the volume integral of a scalar field `f` over a defined volume V.
///
/// The integral is `∫∫∫_V f dV = ∫_x1^x2 ∫_y1^y2 ∫_z1^z2 f(x,y,z) dz dy dx`.
///
/// # Arguments
/// * `scalar_field` - The scalar field `f` as an `Expr`.
/// * `volume` - The `Volume` structure defining the integration domain and order.
///
/// # Returns
/// An `Expr` representing the symbolic volume integral.
#[must_use]
pub fn volume_integral(
    scalar_field: &Expr,
    volume: &Volume,
) -> Expr {
    let (x_var, y_var, z_var) = (&volume.vars.0, &volume.vars.1, &volume.vars.2);

    let integral_z =
        definite_integrate(scalar_field, z_var, &volume.z_bounds.0, &volume.z_bounds.1);

    let integral_y = definite_integrate(&integral_z, y_var, &volume.y_bounds.0, &volume.y_bounds.1);

    let integral_x = definite_integrate(&integral_y, x_var, &volume.x_bounds.0, &volume.x_bounds.1);

    simplify(&integral_x)
}