BELFEM 0.9.3
Berkeley Lab Finite Element Framework
Loading...
Searching...
No Matches
cl_JcFunction_Database.hpp
Go to the documentation of this file.
1/*
2 * BELFEM -- The Berkeley Lab Finite Element Framework
3 * Copyright (c) 2026, The Regents of the University of California,
4 * through Lawrence Berkeley National Laboratory (subject to receipt of any required
5 * approvals from the U.S. Dept. of Energy). All rights reserved.
6 *
7 * Developers: Christian Messe, Gregory Giard
8 *
9 * See the top-level LICENSE file for the complete license and disclaimer.
10 */
11
12#ifndef BELFEM_CL_JCFUNCTION_DATABASE_HPP
13#define BELFEM_CL_JCFUNCTION_DATABASE_HPP
14#include <algorithm>
15#include <cmath>
16#include <memory>
17#include "constants.hpp"
18#include "commtools.hpp"
19#include "cl_JcFunction.hpp"
20#include "cl_Database.hpp"
21#include "fn_min.hpp"
22namespace belfem
23{
24 namespace material
25 {
26//------------------------------------------------------------------------------
27
65 {
66 std::unique_ptr< Database > mDatabase ;
67 const real mTmin ;
68 const real mTmax ;
69 const real mBmin ;
70 const real mBmax ;
71 const real mAngleMin ;
72 const real mAngleMax ;
73 const real mInvLn10 = 1.0/std::log( 10. );
74
79 Vector< real > mSelfFieldT ;
80 Vector< real > mSelfFieldValue ;
81 Vector< real > mSelfFieldSlope ;
82 bool mHaveSelfField = false ;
83
85 bool mIsN = false ;
86
89 static constexpr real mSmoothWidth = 1e-3 ;
90
94 struct Edge
95 {
96 real V = 0.0 ;
97 real S = 0.0 ;
98 real V_T = 0.0 ;
99 real V_A = 0.0 ;
100 real S_T = 0.0 ;
101 real S_A = 0.0 ;
102 };
103
104
105 public:
106
134 JcFunctionDatabase( const string & aPath, const string & aLabel ) :
135 JcFunction(),
136 mDatabase( open_checked( aPath, aLabel ) ),
137 mTmin( mDatabase->min( 0 ) ),
138 mTmax( mDatabase->max( 0 ) ),
139 mBmin( std::pow( 10., mDatabase->min( 1 ) ) ),
140 mBmax( std::pow( 10., mDatabase->max( 1 ) ) ),
141 mAngleMin( mDatabase->min( 2 ) ),
142 mAngleMax( mDatabase->max( 2 ) )
143 {
147
148 mIsN = aLabel != "jc" ;
149
150 // n continues from the table alone; jc anchors on the measured
151 // self-field curve where the file carries one
152 if ( ! mIsN )
153 {
154 this->load_self_field( aPath, aLabel );
155 if ( mHaveSelfField )
156 {
157 this->finalize_self_field();
158 }
159 }
160 }
161
170 static Database *
171 open_checked( const string & aPath, const string & aLabel )
172 {
173 std::unique_ptr< Database > tDatabase( new Database( aPath, aLabel ) );
174
175 BELFEM_ERROR( tDatabase->dimension() == 3,
176 "database %s in %s has %u axes; a jc or n table needs T, log10 B and theta",
177 aLabel.c_str(), aPath.c_str(), ( unsigned int ) tDatabase->dimension() );
178
179 // the caller delivers the UNFOLDED angle theta in [ 0, pi ]; a
180 // folded export covering less than that range would silently
181 // wrap or extrapolate. Coverage test, not a width test:
182 // [ -pi/2, pi/2 ] has width pi and is still wrong
183 BELFEM_ERROR( tDatabase->min( 2 ) <= 1e-6
184 && tDatabase->max( 2 ) >= constant::pi - 1e-6,
185 "angular range of database %s spans [ %g, %g ] rad, but the unfolded\n"
186 "bn_angle contract requires coverage of [ 0, pi ]: a folded export\n"
187 "would silently mis-sample theta > pi/2",
188 aLabel.c_str(), ( double ) tDatabase->min( 2 ), ( double ) tDatabase->max( 2 ) );
189
190 // the continuation below Bmin is monotone only if the table does
191 // not rise with |B| on its lowest-field face. Certified over the
192 // whole face ( Bernstein bound ), not sampled; the tolerance
193 // accepts the rounding residue of a zero slope
194 const real tSlopeBound = tDatabase->upper_bound_derivy_on_lower_y_face() ;
195 BELFEM_ERROR( tSlopeBound <= 1e-10,
196 "database %s in %s cannot be certified not to rise with |B| at its "
197 "lowest field ( bound on d log10 value / d log10 B: %g ), so it "
198 "cannot be continued monotonically below Bmin",
199 aLabel.c_str(), aPath.c_str(), ( double ) tSlopeBound );
200
201 if ( aLabel != "jc" )
202 {
203 // n below Bmin is at least its value at Bmin, which must
204 // keep the power law a power law
205 const real tFloor = tDatabase->lower_bound_on_lower_y_face() ;
206 BELFEM_ERROR( tFloor >= std::log10( 1.01 ),
207 "database %s in %s reaches n = %g at its lowest field; n must be at least 1.01",
208 aLabel.c_str(), aPath.c_str(), ( double ) std::pow( 10., tFloor ) );
209 }
210
211 return tDatabase.release() ;
212 }
213
214
229 void
230 load_self_field( const string & aPath,
231 const string & aLabel )
232 {
233 // Which raw column carries this function: the jc table is built
234 // from the sheet current per width and needs the layer thickness
235 // to become a current density; n is dimensionless and is read
236 // straight. n is NOT given the jc treatment -- its low-field
237 // behaviour is its own.
238 const bool tIsJc = aLabel == "jc" ;
239 const string tColumn = tIsJc ? "icw" : "n" ;
240
241 Vector< real > tT ;
242 Vector< real > tValue ;
243
244 #ifdef BELFEM_HDF5
245 if ( comm_rank() == 0 )
246 {
247 // raw handle rather than the HDF5 wrapper: /source is a
248 // SIBLING of the group Database opened, and the wrapper
249 // exposes no root handle to probe. Same idiom as
250 // fn_rho_database_is_current.hpp
251 hid_t tFile = H5Fopen( aPath.c_str(),
252 H5F_ACC_RDONLY, H5P_DEFAULT );
253
254 if ( tFile >= 0 )
255 {
256 // tri-state, as in rho_database_is_current: only a
257 // definite hit counts, a probe error falls through to
258 // the historical clamp
259 if ( H5Lexists( tFile, "source", H5P_DEFAULT ) > 0 )
260 {
261 hid_t tGroup = H5Gopen2( tFile, "source", H5P_DEFAULT );
262
263 if ( tGroup >= 0 )
264 {
265 herr_t tStatus = 0 ;
266
267 if ( hdf5::dataset_exists( tGroup, "points" )
268 && hdf5::dataset_exists( tGroup, "columns" ) )
269 {
270 Cell< string > tColumns ;
272 tGroup, "columns", tColumns, tStatus );
273
274 Matrix< real > tPoints ;
276 tGroup, "points", tPoints, tStatus );
277
278 // columns located by NAME, never by position:
279 // the raw schema belongs to the data source
280 index_t tIt = tColumns.size() ;
281 index_t tIb = tColumns.size() ;
282 index_t tIv = tColumns.size() ;
283
284 for ( index_t k = 0; k < tColumns.size(); ++k )
285 {
286 if ( tColumns( k ) == "temperature" ) tIt = k ;
287 if ( tColumns( k ) == "field" ) tIb = k ;
288 if ( tColumns( k ) == tColumn ) tIv = k ;
289 }
290
291 // the schema promises columns.size() == points.n_cols,
292 // but a malformed file must not become out-of-bounds
293 // access in a release build
294 if ( tIt < tPoints.n_cols()
295 && tIb < tPoints.n_cols()
296 && tIv < tPoints.n_cols()
297 && tIt < tColumns.size()
298 && tIb < tColumns.size()
299 && tIv < tColumns.size() )
300 {
301 // reduce UNSCALED first; the calibration
302 // below needs the curve's own 77.5 K value
304 tPoints, tIt, tIb, tIv,
305 1.0, tT, tValue );
306
307 if ( tIsJc && tT.length() > 0 )
308 {
309 // The raw column's UNIT belongs to the data
310 // vendor, not to us -- sp-ap ships icw in
311 // A/cm while meta declares A/m, and assuming
312 // either silently breaks the other
313 // ( measured: reading A/cm as A/m left J0
314 // 100x low and the monotonicity guard
315 // clamped every call ). So the curve is
316 // CALIBRATED: scaled so its 77.5 K value
317 // equals the table's own declared level,
318 // Icw_77p5K_sf / t_eff. No unit assumption
319 // survives that.
320 real tScale = 0.0 ;
321
322 if ( hdf5::group_exists( tFile, "meta" ) )
323 {
324 hid_t tMeta = H5Gopen2( tFile, "meta",
325 H5P_DEFAULT );
326 if ( tMeta >= 0 )
327 {
328 real tIcw = 0.0 ;
329 real tTeff = 0.0 ;
330 if ( hdf5::dataset_exists( tMeta,
331 "Icw_77p5K_sf_A_per_m" ) )
332 {
334 tMeta, "Icw_77p5K_sf_A_per_m",
335 tIcw, tStatus );
336 }
337 if ( hdf5::dataset_exists( tMeta,
338 "t_eff_m" ) )
339 {
341 tMeta, "t_eff_m",
342 tTeff, tStatus );
343 }
344 H5Gclose( tMeta );
345
346 if ( tIcw > 0.0 && tTeff > 0.0 )
347 {
348 real tDummy ;
349 const real tCurve77p5 =
350 interp_curve( tT, tValue,
351 77.5, tDummy );
352 if ( tCurve77p5 > 0.0 )
353 {
354 tScale = ( tIcw / tTeff )
355 / tCurve77p5 ;
356 }
357 }
358 }
359 }
360
361 if ( tScale > 0.0 )
362 {
363 for ( belfem::index_t k = 0;
364 k < tValue.length(); ++k )
365 {
366 tValue( k ) *= tScale ;
367 }
368 }
369 else
370 {
371 // no declared level to calibrate against:
372 // keep the clamp rather than guess a unit
373 tT.set_size( 0, 0.0 );
374 tValue.set_size( 0, 0.0 );
375 }
376 }
377 }
378 }
379 H5Gclose( tGroup );
380 }
381 }
382 H5Fclose( tFile );
383 }
384 }
385 #endif
386
387 // the LENGTH is decided on rank 0 and must reach the others
388 // before the payload: a few dozen entries, so broadcast rather
389 // than the chunked share/receive pair
390 uint tN = ( comm_rank() == 0 ) ? ( uint ) tT.length() : 0 ;
391 broadcast( tN );
392
393 if ( tN == 0 ) return ;
394
395 if ( comm_rank() != 0 )
396 {
397 tT.set_size( tN, 0.0 );
398 tValue.set_size( tN, 0.0 );
399 }
400 broadcast( tT );
401 broadcast( tValue );
402
403 mSelfFieldT = tT ;
404 mSelfFieldValue = tValue ;
405 mHaveSelfField = true ;
406 }
407
418 void
420 const Matrix< real > & aPoints,
421 const index_t aColT,
422 const index_t aColB,
423 const index_t aColV,
424 const real aScale,
425 Vector< real > & aT,
426 Vector< real > & aValue ) const
427 {
428 const index_t tNumRows = aPoints.n_rows() ;
429
430 // one setpoint's temperature jitter is a few hundredths of a
431 // kelvin; the setpoint spacing is 2.5 K
432 constexpr real tTol = 0.25 ;
433
434 Cell< real > tT ;
435 Cell< real > tSum ;
436 Cell< uint > tCount ;
437
438 for ( index_t i = 0; i < tNumRows; ++i )
439 {
440 const real tB = aPoints( i, aColB ) ;
441 const real tTi = aPoints( i, aColT ) ;
442 const real tVi = aPoints( i, aColV ) ;
443
444 // exact self-field rows only. The source carries NaNs in
445 // rows where a fit did not converge; those must not enter
446 // an average
447 if ( tB != 0.0 ) continue ;
448 if ( ! std::isfinite( tTi ) ) continue ;
449 if ( ! std::isfinite( tVi ) ) continue ;
450 if ( tVi <= 0.0 ) continue ;
451
452 index_t tBin = tT.size() ;
453 for ( index_t k = 0; k < tT.size(); ++k )
454 {
455 if ( std::abs( tT( k ) - tTi ) < tTol ) { tBin = k ; break ; }
456 }
457
458 if ( tBin == tT.size() )
459 {
460 tT.push( tTi );
461 tSum.push( tVi );
462 tCount.push( 1 );
463 }
464 else
465 {
466 tSum( tBin ) += tVi ;
467 ++tCount( tBin ) ;
468 }
469 }
470
471 const index_t tNumBins = tT.size() ;
472 if ( tNumBins < 2 ) return ;
473
474 // ascending in T, as self_field() assumes
475 Vector< index_t > tOrder( tNumBins );
476 for ( index_t k = 0; k < tNumBins; ++k ) tOrder( k ) = k ;
477
478 for ( index_t a = 0; a + 1 < tNumBins; ++a )
479 {
480 for ( index_t b = a + 1; b < tNumBins; ++b )
481 {
482 if ( tT( tOrder( b ) ) < tT( tOrder( a ) ) )
483 {
484 const index_t tSwap = tOrder( a ) ;
485 tOrder( a ) = tOrder( b ) ;
486 tOrder( b ) = tSwap ;
487 }
488 }
489 }
490
491 aT.set_size( tNumBins, 0.0 );
492 aValue.set_size( tNumBins, 0.0 );
493
494 for ( index_t k = 0; k < tNumBins; ++k )
495 {
496 const index_t j = tOrder( k ) ;
497 aT( k ) = tT( j ) ;
498 aValue( k ) = aScale * tSum( j )
499 / static_cast< real >( tCount( j ) ) ;
500 }
501 }
502
506 ~JcFunctionDatabase() override = default ;
507
510 real
511 min_value() const override
512 {
513 return std::pow( 10., min( mDatabase->values() ) ) ;
514 }
515
516
517 //--------------------------------------------------------------
518 // below Bmin
519 //--------------------------------------------------------------
520 //
521 // The table's field axis is log10 B and ends at Bmin; B = 0 is out
522 // of its reach. Below Bmin the value is continued in LINEAR B from
523 // the table's own value V and slope S = dV/dB at Bmin, so the join
524 // is C1 and nothing at or above Bmin changes. The table does not
525 // rise with |B| on that face ( certified at load ), so S <= 0.
526 //
527 // n: the parabola with value V and slope S at Bmin and slope 0 at
528 // B = 0, n = V + S ( B^2 - Bmin^2 ) / ( 2 Bmin ). n depends on |B|,
529 // so a zero slope at the origin is the smooth choice.
530 //
531 // jc: a cubic Hermite from an anchor J at B = 0 to ( V, S ). The
532 // anchor is the MEASURED angle-free self-field level a( T ) of the
533 // table's /source rows. In the Kim family used throughout the HTS
534 // literature, jc = jc0 / ( 1 + |B_eff| / B0 )^alpha ( Riva et al.
535 // 2023 Eq. 2, Denis et al. 2026 Eq. 11; Messe et al. 2023 §2.6 names
536 // Kim 1962 ), the slope at B = 0 is finite and non-zero, and the
537 // anisotropy vanishes with the field. A monotone C1 join needs
538 // J >= b = V - S Bmin / 3. The measured level sits within a few
539 // percent of b over most of the range and below it at some angles,
540 // and outside the measured temperatures its linear continuation
541 // can fall well short ( at low T and near Tc ), so the anchor is
542 // J = smax( a, b ): the measured level where it clears b, lifted
543 // smoothly to b elsewhere, and angle-dependent only where lifted.
544 // With Delta = ( V - J ) / Bmin < 0 and beta = S / Delta in
545 // [ 0, 3 ), the slope at B = 0 is s0 = 2 Delta - S for beta <= 2
546 // ( the exact quadratic, a finite Kim slope ) and 0 for beta > 2,
547 // which is common wherever the anchor is lifted. Either way
548 // ( alpha, beta ) = ( s0 / Delta, beta ) lies in the square
549 // 0 <= alpha, beta <= 3, which suffices for a monotone cubic
550 // ( Fritsch & Carlson 1980, §4 ), so the join does not rise with
551 // |B| at any point. At beta = 2 the value is continuous but its T
552 // and angle tangents are not. A jc table without a measured curve
553 // keeps the clamp below Bmin.
554
557 static real
559 const Vector< real > & aValue,
560 const real T, real & dJ0dT )
561 {
562 const index_t tN = aT.length() ;
563
564 if ( T <= aT( 0 ) )
565 {
566 dJ0dT = 0.0 ;
567 return aValue( 0 ) ;
568 }
569 if ( T >= aT( tN - 1 ) )
570 {
571 dJ0dT = 0.0 ;
572 return aValue( tN - 1 ) ;
573 }
574
575 index_t k = 1 ;
576 while ( k < tN - 1 && aT( k ) < T ) ++k ;
577
578 const real tDT = aT( k ) - aT( k - 1 ) ;
579 dJ0dT = ( aValue( k ) - aValue( k - 1 ) ) / tDT ;
580
581 return aValue( k - 1 )
582 + dJ0dT * ( T - aT( k - 1 ) ) ;
583 }
584
589 void
591 {
592 const index_t tN = mSelfFieldT.length() ;
593
594 for ( index_t k = tN - 1; k > 0; --k )
595 {
596 mSelfFieldValue( k - 1 ) = std::max( mSelfFieldValue( k - 1 ), mSelfFieldValue( k ) );
597 }
598
599 mSelfFieldSlope.set_size( tN, 0.0 );
600 for ( index_t k = 1; k + 1 < tN; ++k )
601 {
602 const real h0 = mSelfFieldT( k ) - mSelfFieldT( k - 1 ) ;
603 const real h1 = mSelfFieldT( k + 1 ) - mSelfFieldT( k ) ;
604 const real d0 = ( mSelfFieldValue( k ) - mSelfFieldValue( k - 1 ) ) / h0 ;
605 const real d1 = ( mSelfFieldValue( k + 1 ) - mSelfFieldValue( k ) ) / h1 ;
606
607 // zero where the secants differ in sign or one vanishes,
608 // else the weighted harmonic mean
609 if ( d0 * d1 > 0.0 )
610 {
611 const real w0 = 2.0 * h1 + h0 ;
612 const real w1 = h1 + 2.0 * h0 ;
613 mSelfFieldSlope( k ) = ( w0 + w1 ) / ( w0 / d0 + w1 / d1 ) ;
614 }
615 }
616 mSelfFieldSlope( 0 ) = ( mSelfFieldValue( 1 ) - mSelfFieldValue( 0 ) )
617 / ( mSelfFieldT( 1 ) - mSelfFieldT( 0 ) ) ;
618 mSelfFieldSlope( tN - 1 ) = ( mSelfFieldValue( tN - 1 ) - mSelfFieldValue( tN - 2 ) )
619 / ( mSelfFieldT( tN - 1 ) - mSelfFieldT( tN - 2 ) ) ;
620 }
621
624 real
625 self_field( const real T, real & aDT ) const
626 {
627 const index_t tN = mSelfFieldT.length() ;
628
629 if ( T <= mSelfFieldT( 0 ) )
630 {
631 aDT = mSelfFieldSlope( 0 ) ;
632 return mSelfFieldValue( 0 ) + aDT * ( T - mSelfFieldT( 0 ) ) ;
633 }
634 if ( T >= mSelfFieldT( tN - 1 ) )
635 {
636 aDT = mSelfFieldSlope( tN - 1 ) ;
637 return mSelfFieldValue( tN - 1 ) + aDT * ( T - mSelfFieldT( tN - 1 ) ) ;
638 }
639
640 index_t k = 1 ;
641 while ( k < tN - 1 && mSelfFieldT( k ) < T ) ++k ;
642
643 const real h = mSelfFieldT( k ) - mSelfFieldT( k - 1 ) ;
644 const real s = ( T - mSelfFieldT( k - 1 ) ) / h ;
645 const real y0 = mSelfFieldValue( k - 1 ) ;
646 const real y1 = mSelfFieldValue( k ) ;
647 const real m0 = mSelfFieldSlope( k - 1 ) * h ;
648 const real m1 = mSelfFieldSlope( k ) * h ;
649
650 aDT = ( ( 6. * s * s - 6. * s ) * y0 + ( 3. * s * s - 4. * s + 1. ) * m0
651 + ( -6. * s * s + 6. * s ) * y1 + ( 3. * s * s - 2. * s ) * m1 ) / h ;
652
653 return ( 2. * s * s * s - 3. * s * s + 1. ) * y0
654 + ( s * s * s - 2. * s * s + s ) * m0
655 + ( -2. * s * s * s + 3. * s * s ) * y1
656 + ( s * s * s - s * s ) * m1 ;
657 }
658
661 void
662 edge( const real angle, const real T,
663 const bool aWithT, const bool aWithAngle, Edge & aEdge ) const
664 {
665 const real tT = std::clamp( T, mTmin, mTmax ) ;
666 const real tU = mDatabase->min( 1 ) ;
667 const real tA = this->wrap_angle( angle ) ;
668 const real tLn10 = 1.0 / mInvLn10 ;
669
670 const real f_u = mDatabase->evaluate_derivy( tT, tU, tA ) ;
671
672 aEdge.V = std::pow( 10., mDatabase->evaluate( tT, tU, tA ) ) ;
673 aEdge.S = aEdge.V * f_u / mBmin ;
674
675 if ( aWithT && T > mTmin && T < mTmax )
676 {
677 aEdge.V_T = aEdge.V * tLn10 * mDatabase->evaluate_derivx( tT, tU, tA ) ;
678 aEdge.S_T = ( aEdge.V_T * f_u
679 + aEdge.V * mDatabase->evaluate_derivxy( tT, tU, tA ) ) / mBmin ;
680 }
681 if ( aWithAngle )
682 {
683 aEdge.V_A = aEdge.V * tLn10 * mDatabase->evaluate_derivz( tT, tU, tA ) ;
684 aEdge.S_A = ( aEdge.V_A * f_u
685 + aEdge.V * mDatabase->evaluate_derivyz( tT, tU, tA ) ) / mBmin ;
686 }
687 }
688
690 bool
691 continued( const real normB ) const
692 {
693 return normB < mBmin && ( mIsN || mHaveSelfField ) ;
694 }
695
698 real
699 anchor( const real a, const real V, const real S,
700 const real aA_q, const real aV_q, const real aS_q,
701 real & aJ_q ) const
702 {
703 const real b = V - S * mBmin / 3. ;
704 const real b_q = aV_q - aS_q * mBmin / 3. ;
705 // the width scales with b, which is at least V > 0: a width
706 // built on |a| would kink where the continued curve crosses 0
707 const real e = mSmoothWidth * b ;
708 const real e_q = mSmoothWidth * b_q ;
709 const real w = std::sqrt( ( a - b ) * ( a - b ) + e * e ) ;
710
711 aJ_q = 0.5 * ( aA_q + b_q + ( ( a - b ) * ( aA_q - b_q ) + e * e_q ) / w ) ;
712 return 0.5 * ( a + b + w ) ;
713 }
714
717 real
718 jc_join( const real x, const real J, const real V, const real S,
719 const real aJ_q, const real aV_q, const real aS_q,
720 real & aValue_q, real & aDB ) const
721 {
722 const real tDelta = ( V - J ) / mBmin ;
723 const real tBeta = S / tDelta ;
724 const bool tExact = tBeta <= 2.0 ;
725
726 const real s0 = tExact ? 2. * tDelta - S : 0.0 ;
727 const real s0_q = tExact ? 2. * ( aV_q - aJ_q ) / mBmin - aS_q : 0.0 ;
728
729 const real h00 = 2. * x * x * x - 3. * x * x + 1. ;
730 const real h10 = x * x * x - 2. * x * x + x ;
731 const real h01 = -2. * x * x * x + 3. * x * x ;
732 const real h11 = x * x * x - x * x ;
733
734 aValue_q = h00 * aJ_q + h10 * mBmin * s0_q + h01 * aV_q + h11 * mBmin * aS_q ;
735
736 aDB = ( ( 6. * x * x - 6. * x ) * J
737 + ( 3. * x * x - 4. * x + 1. ) * mBmin * s0
738 + ( -6. * x * x + 6. * x ) * V
739 + ( 3. * x * x - 2. * x ) * mBmin * S ) / mBmin ;
740
741 return h00 * J + h10 * mBmin * s0 + h01 * V + h11 * mBmin * S ;
742 }
743
746 real
747 continuation( const real normB, const real angle, const real T,
748 const uint aMode, real & aDerivative ) const
749 {
750 Edge tEdge ;
751 this->edge( angle, T, aMode == 1, aMode == 2, tEdge ) ;
752
753 const real tV_q = aMode == 1 ? tEdge.V_T : tEdge.V_A ;
754 const real tS_q = aMode == 1 ? tEdge.S_T : tEdge.S_A ;
755
756 if ( mIsN )
757 {
758 const real tShape = ( normB * normB - mBmin * mBmin ) / ( 2. * mBmin ) ;
759 aDerivative = aMode == 0 ? tEdge.S * normB / mBmin
760 : tV_q + tS_q * tShape ;
761 return tEdge.V + tEdge.S * tShape ;
762 }
763
764 const bool tInside = T > mTmin && T < mTmax ;
765 real a_T ;
766 const real a = this->self_field( std::clamp( T, mTmin, mTmax ), a_T ) ;
767 const real tA_q = ( aMode == 1 && tInside ) ? a_T : 0.0 ;
768
769 real tJ_q ;
770 const real J = this->anchor( a, tEdge.V, tEdge.S, tA_q, tV_q, tS_q, tJ_q ) ;
771
772 real tValue_q, tDB ;
773 const real tValue = this->jc_join( normB / mBmin, J, tEdge.V, tEdge.S,
774 tJ_q, tV_q, tS_q, tValue_q, tDB ) ;
775
776 aDerivative = aMode == 0 ? tDB : tValue_q ;
777 return tValue ;
778 }
779
791 bool
792 angle_is_snapped( const real angle ) const
793 {
794 return ( angle >= mAngleMin - 1e-6 && angle < mAngleMin )
795 || ( angle > mAngleMax && angle <= mAngleMax + 1e-6 ) ;
796 }
797
798 real
799 wrap_angle( const real angle ) const
800 {
801 return angle < mAngleMin - 1e-6 ? angle + constant::pi :
802 angle > mAngleMax + 1e-6 ? angle - constant::pi :
803 std::clamp( angle, mAngleMin, mAngleMax );
804 }
805
818 real
819 eval( const real normB, const real angle, const real T ) const override
820 {
821 if ( this->continued( normB ) )
822 {
823 real tUnused ;
824 return this->continuation( normB, angle, T, 0, tUnused ) ;
825 }
826
827 return std::pow( 10.,
828 mDatabase->evaluate(
829 std::clamp( T, mTmin, mTmax ),
830 std::log( std::clamp( normB, mBmin, mBmax ) ) * mInvLn10,
831 this->wrap_angle( angle ) ) );
832 }
833
843 real
844 deval_dB( const real normB, const real angle, const real T ) const override
845 {
846 if ( this->continued( normB ) )
847 {
848 real tDB ;
849 this->continuation( normB, angle, T, 0, tDB ) ;
850 return tDB ;
851 }
852
853 if ( normB >= mBmax ) return 0.0 ;
854 if ( normB <= mBmin && ! ( mIsN || mHaveSelfField ) ) return 0.0 ;
855
856 return this->eval( normB, angle, T )
857 * mDatabase->evaluate_derivy(
858 std::clamp( T, mTmin, mTmax ),
859 std::log( normB ) * mInvLn10,
860 this->wrap_angle( angle ) )
861 / normB ;
862 }
863
871 real
872 deval_dbeta( const real normB, const real angle, const real T ) const override
873 {
874 if ( this->angle_is_snapped( angle ) ) return 0.0 ;
875
876 if ( this->continued( normB ) )
877 {
878 real tDA ;
879 this->continuation( normB, angle, T, 2, tDA ) ;
880 return tDA ;
881 }
882
883 return this->eval( normB, angle, T )
884 * mDatabase->evaluate_derivz(
885 std::clamp( T, mTmin, mTmax ),
886 std::log( std::clamp( normB, mBmin, mBmax ) ) * mInvLn10,
887 this->wrap_angle( angle ) )
888 / mInvLn10 ;
889 }
890
897 real
898 deval_dT( const real normB, const real angle, const real T ) const override
899 {
900 if ( T <= mTmin || T >= mTmax ) return 0.0 ;
901
902 if ( this->continued( normB ) )
903 {
904 real tDT ;
905 this->continuation( normB, angle, T, 1, tDT ) ;
906 return tDT ;
907 }
908
909 return this->eval( normB, angle, T )
910 * mDatabase->evaluate_derivx(
911 T,
912 std::log( std::clamp( normB, mBmin, mBmax ) ) * mInvLn10,
913 this->wrap_angle( angle ) )
914 / mInvLn10 ;
915 }
916 };
917 }
918}
919#endif //BELFEM_CL_JCFUNCTIONDATABASE_HPP
#define BELFEM_ERROR(aCheck,...)
Definition assert.hpp:264
Base class hierarchy for critical current density (Jc) and n-value functions.
size_t size() const
Definition cl_Cell.hpp:189
void push(const T &aValue)
Definition cl_Cell.hpp:265
Precomputed lookup table on a tensor grid; evaluation, derivatives and HDF5 persistence.
Definition cl_Database.hpp:30
Database(const string &aFilePath, const string &aMaterial)
Definition cl_Database.cpp:23
size_t n_rows() const
Definition cl_AR_Matrix.hpp:183
size_t n_cols() const
Definition cl_AR_Matrix.hpp:191
void set_size(const size_t aNumRows)
change the size of the vector
Definition cl_AR_Vector.hpp:215
size_t length() const
get the length of the vector
Definition cl_AR_Vector.hpp:235
real deval_dbeta(const real normB, const real angle, const real T) const override
d(value)/dθ, the exact derivative of eval
Definition cl_JcFunction_Database.hpp:872
~JcFunctionDatabase() override=default
Destructor - deletes the database.
bool angle_is_snapped(const real angle) const
Wrap the caller's angle into the table window.
Definition cl_JcFunction_Database.hpp:792
real deval_dT(const real normB, const real angle, const real T) const override
d(value)/dT, the exact derivative of eval
Definition cl_JcFunction_Database.hpp:898
real wrap_angle(const real angle) const
Definition cl_JcFunction_Database.hpp:799
bool continued(const real normB) const
true if B lies on the continuation of this label
Definition cl_JcFunction_Database.hpp:691
real continuation(const real normB, const real angle, const real T, const uint aMode, real &aDerivative) const
Definition cl_JcFunction_Database.hpp:747
static Database * open_checked(const string &aPath, const string &aLabel)
Open the table and refuse one the reader cannot serve.
Definition cl_JcFunction_Database.hpp:171
static real interp_curve(const Vector< real > &aT, const Vector< real > &aValue, const real T, real &dJ0dT)
Definition cl_JcFunction_Database.hpp:558
real anchor(const real a, const real V, const real S, const real aA_q, const real aV_q, const real aS_q, real &aJ_q) const
Definition cl_JcFunction_Database.hpp:699
void reduce_self_field_rows(const Matrix< real > &aPoints, const index_t aColT, const index_t aColB, const index_t aColV, const real aScale, Vector< real > &aT, Vector< real > &aValue) const
Bin the B = 0 rows by temperature and average over angle.
Definition cl_JcFunction_Database.hpp:419
real jc_join(const real x, const real J, const real V, const real S, const real aJ_q, const real aV_q, const real aS_q, real &aValue_q, real &aDB) const
Definition cl_JcFunction_Database.hpp:718
void finalize_self_field()
Definition cl_JcFunction_Database.hpp:590
real eval(const real normB, const real angle, const real T) const override
Evaluate Jc or n at given field, angle, and temperature.
Definition cl_JcFunction_Database.hpp:819
JcFunctionDatabase(const string &aPath, const string &aLabel)
Constructor - loads database from file.
Definition cl_JcFunction_Database.hpp:134
real self_field(const real T, real &aDT) const
Definition cl_JcFunction_Database.hpp:625
void load_self_field(const string &aPath, const string &aLabel)
Read the measured B = 0 level from the table's /source.
Definition cl_JcFunction_Database.hpp:230
real deval_dB(const real normB, const real angle, const real T) const override
d(value)/d|B|, the exact derivative of eval
Definition cl_JcFunction_Database.hpp:844
void edge(const real angle, const real T, const bool aWithT, const bool aWithAngle, Edge &aEdge) const
Definition cl_JcFunction_Database.hpp:662
real min_value() const override
Definition cl_JcFunction_Database.hpp:511
void set_dependency(const JcParameter aParameter)
Mark function as depending on a parameter.
Definition cl_JcFunction.cpp:20
Smallest entry of a vector, matrix or column view.
const real pi
circle number
Definition constants.hpp:41
bool group_exists(hid_t aLoc, const std::string &aLabel)
test if a group exists ( tests for a link of that name; the link type is not checked )
Definition hdf5_tools.hpp:94
void load_scalar_from_file(hid_t &aLoc, const std::string &aLabel, T &aValue, herr_t &aStatus)
Definition hdf5_tools.hpp:331
void load_strings_from_file(hid_t &aLoc, const std::string &aLabel, Cell< string > &aCell, herr_t &aStatus)
Definition hdf5_tools.hpp:956
void load_matrix_from_file(hid_t &aLoc, const std::string &aLabel, Matrix< T > &aMatrix, herr_t &aStatus, const bool aTranspose=false)
Definition hdf5_tools.hpp:808
bool dataset_exists(hid_t aLoc, const std::string &aLabel)
test if a dataset exists ( tests for a link of that name; the link type is not checked )
Definition hdf5_tools.hpp:77
Definition cl_BhCurve.cpp:19
@ angleNxB
Definition cl_JcFunction.hpp:82
@ normB
Definition cl_JcFunction.hpp:81
@ T
Definition cl_JcFunction.hpp:83
USER GUIDES:
Definition cl_Capacitor.cpp:16
proc_t comm_rank()
Definition commtools.cpp:30
int hid_t
Definition hdf5_types.hpp:20
unsigned int uint
Definition typedefs.hpp:30
void broadcast(Cell< string > &aData, const proc_t aRoot)
Collective: every rank calls it, and aRoot's data lands on all ranks.
Definition commtools.cpp:276
T max(const Vector< T > &aVector)
Definition fn_AR_max.hpp:24
T min(const Vector< T > &aVector)
Definition fn_AR_min.hpp:25
uint32_t index_t
Definition typedefs.hpp:52
int herr_t
Definition hdf5_types.hpp:21
double real
Definition typedefs.hpp:36