/* * EasyWave - A realtime tsunami simulation program with GPU support. * Copyright (C) 2014 Andrey Babeyko, Johannes Spazier * GFZ German Research Centre for Geosciences (http://www.gfz-potsdam.de) * * Parts of this program (especially the GPU extension) were developed * within the context of the following publicly funded project: * - TRIDEC, EU 7th Framework Programme, Grant Agreement 258723 * (http://www.tridec-online.eu) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include "cOkadaFault.h" static const double Rearth=6384.e+3; //========================================================================= // Constructor cOkadaFault::cOkadaFault() { // obligatory user parameters lat = lon = depth = strike = dip = rake = RealMax; // optional user parameters mw = slip = length = width = 0.; adjust_depth = 0; // default values refpos = FLT_POS_C; mu = 3.5e+10; // derivative parameters zbot = sind = cosd = sins = coss = tand = coslat = wp = dslip = sslip = 0; // flags checked = 0; } //========================================================================= // Destructor cOkadaFault::~cOkadaFault() { } //========================================================================= // read fault parameters from input string. read only. consistency check in separate method int cOkadaFault::read( char *faultparam ) { char *cp,buf[64]; int ierr; // origin location cp = strstr( faultparam, "-location" ); if( cp ) { if( sscanf( cp, "%*s %lf %lf %lf", &lon, &lat, &depth ) != 3 ) return Err.post( "cOkadaFault::read: position" ); depth *= 1000; } // adjust depth if fault breaks through surface cp = strstr( faultparam, "-adjust_depth" ); if( cp ) adjust_depth = 1; // reference point cp = strstr( faultparam, "-refpos" ); if( cp ) { if( sscanf( cp, "%*s %s", buf ) != 1 ) return Err.post( "cOkadaFault::read: refpos" ); if( !strcmp( buf, "C" ) || !strcmp( buf, "c" ) ) refpos = FLT_POS_C; else if( !strcmp( buf, "MT" ) || !strcmp( buf, "mt" ) ) refpos = FLT_POS_MT; else if( !strcmp( buf, "BT" ) || !strcmp( buf, "bt" ) ) refpos = FLT_POS_BT; else if( !strcmp( buf, "BB" ) || !strcmp( buf, "bb" ) ) refpos = FLT_POS_BB; else if( !strcmp( buf, "MB" ) || !strcmp( buf, "mb" ) ) refpos = FLT_POS_MB; else return Err.post( "cOkadaFault::read: refpos" ); } // magnitude cp = strstr( faultparam, "-mw" ); if( cp ) if( sscanf( cp, "%*s %lf", &mw ) != 1 ) return Err.post( "cOkadaFault::read: mw" ); // slip cp = strstr( faultparam, "-slip" ); if( cp ) { if( sscanf( cp, "%*s %lf", &slip ) != 1 ) return Err.post( "cOkadaFault::read: slip" ); if( slip < 1.e-6 ) slip = 1.e-6; } // strike cp = strstr( faultparam, "-strike" ); if( cp ) if( sscanf( cp, "%*s %lf", &strike ) != 1 ) return Err.post( "cOkadaFault::read: strike" ); // dip cp = strstr( faultparam, "-dip" ); if( cp ) if( sscanf( cp, "%*s %lf", &dip ) != 1 ) return Err.post( "cOkadaFault::read: dip" ); // rake cp = strstr( faultparam, "-rake" ); if( cp ) if( sscanf( cp, "%*s %lf", &rake ) != 1 ) return Err.post( "cOkadaFault::read: rake" ); // length and width cp = strstr( faultparam, "-size" ); if( cp ) { if( sscanf( cp, "%*s %lf %lf", &length, &width ) != 2 ) return Err.post( "cOkadaFault::read: size" ); length *= 1000; width *= 1000; } // rigidity cp = strstr( faultparam, "-rigidity" ); if( cp ) if( sscanf( cp, "%*s %lf", &mu ) != 1 ) return Err.post( "cOkadaFault::read: rigidity" ); // check fault data for integrity //ierr = check(); if(ierr) return ierr; return 0; } //================================================================================ int cOkadaFault::check() // Check readed fault parameters for consistency and calculate secondary parameters { // check necessary parameters if( lon == RealMax ) { Err.post( "cOkadaFault::check: lon" ); return FLT_ERR_DATA; } if( lat == RealMax ) { Err.post( "cOkadaFault::check: lat" ); return FLT_ERR_DATA; } if( depth == RealMax ) { Err.post( "cOkadaFault::check: depth" ); return FLT_ERR_DATA; } if( strike == RealMax ) { Err.post( "cOkadaFault::check: strike" ); return FLT_ERR_STRIKE; } if( rake == RealMax ) { Err.post( "cOkadaFault::check: rake" ); return FLT_ERR_DATA; } if( dip == RealMax ) { Err.post( "cOkadaFault::check: dip" ); return FLT_ERR_DATA; } // cache trigonometric expressions sind = sindeg( dip ); cosd = cosdeg( dip ); sins = sindeg( 90-strike ); coss = cosdeg( 90-strike ); tand = tandeg( dip ); coslat = cosdeg( lat ); // branching through given parameters (the full solution table see end of file) if( !mw && !slip ) { Err.post( "cOkadaFault::check: not enough data" ); return FLT_ERR_DATA; } else if( !mw && slip ) { if( !length && !width ) { Err.post( "cOkadaFault::check: not enough data" ); return FLT_ERR_DATA; } else if( length && !width ) { width = length/2; } else if( !length && width ) { length = 2*width; } else if( length && width ) { } else { Err.post( "cOkadaFault::check: internal error" ); return FLT_ERR_INTERNAL; } mw = 2./3. * (log10(mu*length*width*slip) - 9.1); } else if( mw && !slip ) { if( !length && !width ) { // scaling relations used by JMA length = pow( 10., -1.80 + 0.5*mw ) * 1000; width = length/2; } else if( length && !width ) { width = length/2; } else if( !length && width ) { length = 2*width; } else if( length && width ) { } else { Err.post( "cOkadaFault::check: internal error" ); return FLT_ERR_INTERNAL; } slip = mw2m0() / mu / length / width; } else if( mw && slip ) { if( !length && !width ) { double area = mw2m0() / mu / slip; length = sqrt( 2 * area ); width = length/2; } else if( length && !width ) { width = mw2m0() / mu / slip / length; } else if( !length && width ) { length = mw2m0() / mu / slip / width; } else if( length && width ) { if( fabs( 1 - mu*slip*length*width/mw2m0() ) > 0.01 ) { Err.post( "cOkadaFault::check: data inconsistency" ); return FLT_ERR_DATA; } } else { Err.post( "cOkadaFault::check: internal error" ); return FLT_ERR_INTERNAL; } } // calculate bottom of the fault switch( refpos ) { double ztop; case FLT_POS_C: ztop = depth - width/2*sind; if( ztop < 0 ) { if( adjust_depth ) { ztop = 0.; depth = ztop + width/2*sind; } else { Err.post( "cOkadaFault::check: negative ztop" ); return FLT_ERR_ZTOP; } } zbot = depth + width/2*sind; break; case FLT_POS_MT: case FLT_POS_BT: zbot = depth + width*sind; break; case FLT_POS_BB: case FLT_POS_MB: ztop = depth - width*sind; if( ztop < 0 ) { if( adjust_depth ) { ztop = 0.; depth = ztop + width*sind; } else { Err.post( "cOkadaFault::check: negative ztop" ); return FLT_ERR_ZTOP; } } zbot = depth; break; } // slip components dslip = slip*sindeg(rake); sslip = slip*cosdeg(rake); // surface projection of width wp = width*cosd; checked = 1; return 0; } //========================================================================= double cOkadaFault::mw2m0() { return pow(10., 3.*mw/2 + 9.1); } //========================================================================= double cOkadaFault::getM0() { if( !checked ) { Err.post( "cOkadaFault::getM0: fault not checked" ); return -RealMax; } return mw2m0(); } //========================================================================= double cOkadaFault::getMw() { if( !checked ) { Err.post( "cOkadaFault::getMw: fault not checked" ); return -RealMax; } return mw; } //========================================================================= double cOkadaFault::getZtop() { return( zbot - width*sind ); } //========================================================================= int cOkadaFault::global2local( double glon, double glat, double& lx, double& ly ) // from global geographical coordinates into local Okada's coordinates { double x,y; // center coordinate system to refpos (lon/lat), convert to meters y = Rearth * g2r(glat - lat); x = Rearth * coslat * g2r(glon - lon); // rotate according to strike lx = x*coss + y*sins; ly = -x*sins + y*coss; // finally shift to Okada's origin point (BB) switch( refpos ) { case FLT_POS_C: lx = lx + length/2; ly = ly + wp/2; break; case FLT_POS_MT: lx = lx + length/2; ly = ly + wp ; break; case FLT_POS_BT: lx = lx ; ly = ly + wp ; break; case FLT_POS_BB: lx = lx ; ly = ly ; break; case FLT_POS_MB: lx = lx + length/2; ly = ly ; break; } return 0; } //========================================================================= int cOkadaFault::local2global( double lx, double ly, double& glon, double& glat ) // from local Okada's coordinates to global geographical { double x,y,gx,gy; // define local coordinates relative to the fault refpos switch( refpos ) { case FLT_POS_C: x = lx - length/2; y = ly - wp/2; break; case FLT_POS_MT: x = lx - length/2; y = ly - wp ; break; case FLT_POS_BT: x = lx ; y = ly - wp ; break; case FLT_POS_BB: x = lx ; y = ly ; break; case FLT_POS_MB: x = lx - length/2; y = ly ; break; } // back-rotate to geographical axes (negative strike!). Values are still in meters! gx = x*coss + y*(-sins); gy = -x*(-sins) + y*coss; // convert meters to degrees. This is offset in degrees relative to refpos. Add refpos coordinates for absolute values glat = r2g(gy/Rearth) + lat; glon = r2g(gx/Rearth/cosdeg(lat)) + lon; return 0; } //========================================================================= // get ruptured area to calculate surface displacement // parameter rand accounts for lateral enlargement at rands of rupture surface projection int cOkadaFault::getDeformArea( double& lonmin, double& lonmax, double& latmin, double& latmax ) { #define FLT_NL 2 // significant deformation area along length (in length units) #define FLT_NW 5 // significant deformation area along width (in width units) int ierr; double dxC,dyC,l2,w2,glon,glat; if( !checked ) { Err.post( "cOkadaFault::getDeformArea: attempt with non-checked fault" ); return FLT_ERR_INTERNAL; } // follow rectangle around the fault dxC = FLT_NL * length; l2 = length/2; dyC = FLT_NW * wp; w2 = wp/2; local2global( dxC+l2, dyC+w2, glon, glat ); lonmin = lonmax = glon; latmin = latmax = glat; local2global( -dxC+l2, dyC+w2, glon, glat ); if( glonlonmax ) lonmax = glon; if( glatlatmax ) latmax = glat; local2global( -dxC+l2, -dyC+w2, glon, glat ); if( glonlonmax ) lonmax = glon; if( glatlatmax ) latmax = glat; local2global( dxC+l2, -dyC+w2, glon, glat ); if( glonlonmax ) lonmax = glon; if( glatlatmax ) latmax = glat; return 0; } //========================================================================= int cOkadaFault::calculate( double lon0, double lat0, double& uz, double& ulon, double &ulat ) { int ierr; double x,y,ux,uy; if( !checked ) { Err.post( "cOkadaFault::calculate: attempt with non-checked fault" ); return FLT_ERR_INTERNAL; } global2local( lon0, lat0, x, y ); // Okada model okada( length, width, zbot, sind, cosd, sslip, dslip, x, y, 1, &ux,&uy,&uz ); // back-rotate horizontal deformations to global coordinates (negative strike!) ulon = ux*coss + uy*(-sins); ulat = -ux*(-sins) + uy*coss; return 0; } //========================================================================= int cOkadaFault::calculate( double lon0, double lat0, double &uz ) { int ierr; double x,y,ux,uy; if( !checked ) { Err.post( "cOkadaFault::calculate: attempt with non-checked fault" ); return FLT_ERR_INTERNAL; } global2local( lon0, lat0, x, y ); // Okada model okada( length, width, zbot, sind, cosd, sslip, dslip, x, y, 0, &ux,&uy,&uz ); return 0; } //====================================================================================== // Input parameter selection: Solution table // if given: // mw slip L W action // - - - - err_data // - - - + err_data // - - + - err_data // - - + + err_data // - + - - err_data // - + - + L=2W, Mw // - + + - W=L/2, Mw // - + + + Mw // + - - - W&C96(L,W), S // + - - + L=2W, S // + - + - W=L/2, S // + - + + S // + + - - area(Mw), L=2W // + + - + L // + + + - W // + + + + check Mw=muSLW